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
package/generated/cli.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
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.program = exports.init = exports.watch = void 0;
|
|
7
|
+
exports.build = build;
|
|
8
|
+
exports.dispatch = dispatch;
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const package_info_js_1 = require("./package-info.js");
|
|
13
|
+
const lsp_js_1 = require("./lsp.js");
|
|
14
|
+
const preprocess_js_1 = require("./preprocess.js");
|
|
15
|
+
const intellisense_js_1 = require("./intellisense.js");
|
|
16
|
+
const rojo_mapper_js_1 = require("./utils/rojo-mapper.js");
|
|
17
|
+
const process_orchestrator_js_1 = require("./utils/process-orchestrator.js");
|
|
18
|
+
const project_js_1 = require("./utils/project.js");
|
|
19
|
+
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return project_js_1.init; } });
|
|
20
|
+
Object.defineProperty(exports, "watch", { enumerable: true, get: function () { return project_js_1.watch; } });
|
|
21
|
+
const transpile_js_1 = require("./transpile.js");
|
|
22
|
+
const program = new commander_1.Command();
|
|
23
|
+
exports.program = program;
|
|
24
|
+
program
|
|
25
|
+
.name("cluaupp")
|
|
26
|
+
.description("Definitive transpiler from C++ subset to structured Luau")
|
|
27
|
+
.version(package_info_js_1.pkg.version, "-v, --version", "print version")
|
|
28
|
+
.showHelpAfterError()
|
|
29
|
+
.action(() => {
|
|
30
|
+
program.outputHelp();
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command("init")
|
|
34
|
+
.description("create a game (src/server, src/client, src/shared)")
|
|
35
|
+
.argument("[folder]", "destination folder", ".")
|
|
36
|
+
.action(async (folder) => {
|
|
37
|
+
try {
|
|
38
|
+
await (0, project_js_1.init)(node_path_1.default.resolve(process.cwd(), folder));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.error(err instanceof Error ? err.message : err);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
program
|
|
46
|
+
.command("build")
|
|
47
|
+
.description("Transpila um arquivo, diretório ou projeto C++ para Luau")
|
|
48
|
+
.argument("[folder]", "project folder", ".")
|
|
49
|
+
.option("-i, --input <path>", "Arquivo ou diretório C++ de entrada")
|
|
50
|
+
.option("-o, --output <path>", "Arquivo ou diretório Luau de saída")
|
|
51
|
+
.option("-r, --rojo <path>", "Caminho para o default.project.json do Rojo", "./default.project.json")
|
|
52
|
+
.option("--strict", "Emitir --!strict")
|
|
53
|
+
.option("--format", "Rodar StyLua no output")
|
|
54
|
+
.option("--analyze", "Rodar luau-analyze no output")
|
|
55
|
+
.action(async (folder, options) => {
|
|
56
|
+
if (options.input) {
|
|
57
|
+
await buildInput(options);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
(0, project_js_1.build)(node_path_1.default.resolve(process.cwd(), folder), {
|
|
62
|
+
format: options.format === true,
|
|
63
|
+
analyze: options.analyze === true,
|
|
64
|
+
rojo: options.rojo,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(err instanceof Error ? err.message : err);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
program
|
|
73
|
+
.command("watch")
|
|
74
|
+
.description("rebuild on save")
|
|
75
|
+
.argument("[folder]", "project folder", ".")
|
|
76
|
+
.option("-r, --rojo <path>", "Caminho para o default.project.json do Rojo", "./default.project.json")
|
|
77
|
+
.option("--format", "Rodar StyLua no output")
|
|
78
|
+
.action((folder, options) => {
|
|
79
|
+
try {
|
|
80
|
+
(0, project_js_1.watch)(node_path_1.default.resolve(process.cwd(), folder), {
|
|
81
|
+
format: options.format === true,
|
|
82
|
+
rojo: options.rojo,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.error(err instanceof Error ? err.message : err);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
program
|
|
91
|
+
.command("lsp")
|
|
92
|
+
.description("Cluaupp subset diagnostics over stdio (JSON-RPC). C++ completion is clangd.")
|
|
93
|
+
.argument("[folder]", "project folder", ".")
|
|
94
|
+
.action((folder) => {
|
|
95
|
+
(0, lsp_js_1.start)({ projectRoot: node_path_1.default.resolve(process.cwd(), folder) });
|
|
96
|
+
});
|
|
97
|
+
program
|
|
98
|
+
.command("intellisense")
|
|
99
|
+
.alias("intelisense")
|
|
100
|
+
.description("install LLVM clangd and write compile_commands.json")
|
|
101
|
+
.argument("[folder]", "project folder", ".")
|
|
102
|
+
.action(async (folder) => {
|
|
103
|
+
try {
|
|
104
|
+
const root = node_path_1.default.resolve(process.cwd(), folder);
|
|
105
|
+
(0, intellisense_js_1.syncEditorSupport)(root, (0, project_js_1.loadConfig)(root));
|
|
106
|
+
const installed = await (0, intellisense_js_1.installEditorSupport)();
|
|
107
|
+
console.log("cluaupp: compile_commands.json, .clangd, and .vscode updated in", root);
|
|
108
|
+
console.log("cluaupp: C++ completion is clangd (include/cluaupp/roblox.hpp)");
|
|
109
|
+
if (installed.clangd || installed.llvm) {
|
|
110
|
+
console.log("cluaupp: reload Cursor (Ctrl+Shift+P → Developer: Reload Window)");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
console.error(err instanceof Error ? err.message : err);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
async function buildInput(options) {
|
|
119
|
+
if (!options.input || !options.output) {
|
|
120
|
+
console.error("[Erro] --input e --output são obrigatórios neste modo.");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const inputPath = node_path_1.default.resolve(options.input);
|
|
124
|
+
const outputPath = node_path_1.default.resolve(options.output);
|
|
125
|
+
try {
|
|
126
|
+
await promises_1.default.stat(inputPath);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
console.error(`[Erro] O caminho de entrada especificado não existe: ${inputPath}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
console.log("🏁 Inicializando Pipeline do Compilador Cluaupp V2...");
|
|
133
|
+
const mapper = new rojo_mapper_js_1.RojoMapper(node_path_1.default.resolve(options.rojo), node_path_1.default.dirname(inputPath));
|
|
134
|
+
await mapper.load();
|
|
135
|
+
const inputs = (await promises_1.default.stat(inputPath)).isDirectory()
|
|
136
|
+
? (0, project_js_1.collectCpp)(inputPath)
|
|
137
|
+
: [inputPath];
|
|
138
|
+
if (inputs.length === 0) {
|
|
139
|
+
console.error("no .cpp/.h/.hpp files in", inputPath);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
const outputIsFile = /\.luau$/i.test(outputPath);
|
|
143
|
+
if (outputIsFile && inputs.length > 1) {
|
|
144
|
+
console.error("[Erro] --output deve ser um diretório quando a entrada contém vários arquivos.");
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
for (const file of inputs) {
|
|
148
|
+
const sourceCode = await promises_1.default.readFile(file, "utf8");
|
|
149
|
+
const rel = node_path_1.default.basename(file);
|
|
150
|
+
const compiled = (0, transpile_js_1.transpileSource)(sourceCode, rel, {
|
|
151
|
+
strict: options.strict === true,
|
|
152
|
+
filePath: file,
|
|
153
|
+
relativeName: rel,
|
|
154
|
+
srcDir: node_path_1.default.dirname(file),
|
|
155
|
+
includeDirs: [node_path_1.default.dirname(file)],
|
|
156
|
+
}, mapper);
|
|
157
|
+
const dest = outputIsFile ? outputPath : node_path_1.default.join(outputPath, node_path_1.default.basename((0, preprocess_js_1.toLuauPath)(rel)));
|
|
158
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(dest), { recursive: true });
|
|
159
|
+
const luau = compiled.files[0]?.contents ?? "";
|
|
160
|
+
await promises_1.default.writeFile(dest, luau, "utf8");
|
|
161
|
+
if (options.format === true) {
|
|
162
|
+
await process_orchestrator_js_1.ProcessOrchestrator.formatWithStyLua(dest);
|
|
163
|
+
}
|
|
164
|
+
if (options.analyze === true) {
|
|
165
|
+
const analysisReport = await process_orchestrator_js_1.ProcessOrchestrator.analyzeWithLuau(dest);
|
|
166
|
+
if (analysisReport) {
|
|
167
|
+
console.log("\nRelatório de Análise Estática do Luau:\n", analysisReport);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log(`Transpilação concluída! ${file} → ${dest}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function build(root, options = {}) {
|
|
174
|
+
return (0, project_js_1.build)(root, options);
|
|
175
|
+
}
|
|
176
|
+
function dispatch(args) {
|
|
177
|
+
return program.parseAsync(["node", "cluaupp", ...args]);
|
|
178
|
+
}
|
|
179
|
+
function isMain() {
|
|
180
|
+
const entry = process.argv[1];
|
|
181
|
+
if (!entry) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return ["cli.js", "cli.ts", "cluaupp.js", "cluau.js"].includes(node_path_1.default.basename(entry));
|
|
185
|
+
}
|
|
186
|
+
if (isMain()) {
|
|
187
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
188
|
+
console.error(err instanceof Error ? err.message : err);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
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.compileSource = compileSource;
|
|
7
|
+
exports.compileService = compileService;
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const parse_js_1 = require("./parse.js");
|
|
10
|
+
const emit_js_1 = require("./emit.js");
|
|
11
|
+
const preprocess_js_1 = require("./preprocess.js");
|
|
12
|
+
const libs_js_1 = require("./libs.js");
|
|
13
|
+
const architecture_js_1 = require("./architecture.js");
|
|
14
|
+
const headers_js_1 = require("./headers.js");
|
|
15
|
+
function shouldAttachRequires(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
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function attachRequires(contents, source, ast, options, outName) {
|
|
27
|
+
if (!shouldAttachRequires(outName, contents)) {
|
|
28
|
+
return contents;
|
|
29
|
+
}
|
|
30
|
+
const libraries = String(contents).includes("CluauppLibs") ? [] : (0, libs_js_1.collectLibraries)(source, ast);
|
|
31
|
+
return (0, libs_js_1.insertPreamble)(contents, options.moduleIncludes || [], libraries, outName);
|
|
32
|
+
}
|
|
33
|
+
function posixRel(from, file) {
|
|
34
|
+
return node_path_1.default.relative(from, file).replace(/\\/g, "/");
|
|
35
|
+
}
|
|
36
|
+
function wireHeaderImpl(ast, fileName, options) {
|
|
37
|
+
const headerPath = options.filePath || null;
|
|
38
|
+
const impl = headerPath ? (0, preprocess_js_1.siblingImplementation)(headerPath) : null;
|
|
39
|
+
if (!impl) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const typeName = (0, headers_js_1.primaryHeaderName)(ast, options.relativeName || fileName);
|
|
43
|
+
const implRel = options.srcDir ? posixRel(options.srcDir, impl) : node_path_1.default.basename(impl).replace(/\\/g, "/");
|
|
44
|
+
const implOut = (0, preprocess_js_1.implOutName)(implRel);
|
|
45
|
+
options.headerImplName = `${typeName}Module`;
|
|
46
|
+
options.moduleIncludes = options.moduleIncludes || [];
|
|
47
|
+
if (!options.moduleIncludes.some((spec) => spec.name === options.headerImplName)) {
|
|
48
|
+
options.moduleIncludes.push({
|
|
49
|
+
name: options.headerImplName,
|
|
50
|
+
outRel: implOut,
|
|
51
|
+
exports: { consts: [], structs: [] },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function compileHeader(source, fileName, options, ast) {
|
|
56
|
+
wireHeaderImpl(ast, fileName, options);
|
|
57
|
+
const rel = (options.relativeName || fileName || "header.h").replace(/\\/g, "/");
|
|
58
|
+
const outName = (0, preprocess_js_1.toLuauPath)(rel);
|
|
59
|
+
const luau = attachRequires((0, headers_js_1.emitHeaderModule)(ast, options), source, ast, options, outName);
|
|
60
|
+
return {
|
|
61
|
+
kind: "module",
|
|
62
|
+
plan: { kind: "module", strict: options.strict === true },
|
|
63
|
+
files: [{ name: outName, contents: luau }],
|
|
64
|
+
stale: [],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function compileSource(source, fileName, options = {}) {
|
|
68
|
+
const opts = options;
|
|
69
|
+
opts.moduleIncludes = opts.moduleIncludes || [];
|
|
70
|
+
const prepared = (0, preprocess_js_1.preprocess)(source, opts.filePath || null, opts);
|
|
71
|
+
const ast = (0, parse_js_1.parse)(prepared, fileName || "input.cpp");
|
|
72
|
+
if ((0, preprocess_js_1.isHeaderFile)(fileName || "") || (0, preprocess_js_1.isHeaderFile)(opts.filePath || "")) {
|
|
73
|
+
return compileHeader(source, fileName || "header.h", opts, ast).files[0].contents;
|
|
74
|
+
}
|
|
75
|
+
const luau = (0, emit_js_1.emit)(ast, opts);
|
|
76
|
+
return attachRequires(luau, source, ast, opts, opts.outName || fileName || "input.cpp");
|
|
77
|
+
}
|
|
78
|
+
function compileService(source, fileName, options = {}) {
|
|
79
|
+
const opts = options;
|
|
80
|
+
opts.moduleIncludes = opts.moduleIncludes || [];
|
|
81
|
+
const prepared = (0, preprocess_js_1.preprocess)(source, opts.filePath || null, opts);
|
|
82
|
+
const ast = (0, parse_js_1.parse)(prepared, fileName || "input.cpp");
|
|
83
|
+
if ((0, preprocess_js_1.isHeaderFile)(fileName || "") || (0, preprocess_js_1.isHeaderFile)(opts.filePath || "") || (0, preprocess_js_1.isHeaderFile)(opts.relativeName || "")) {
|
|
84
|
+
return compileHeader(source, fileName || "header.h", opts, ast);
|
|
85
|
+
}
|
|
86
|
+
const implHeader = opts.filePath ? (0, preprocess_js_1.siblingHeader)(opts.filePath) : null;
|
|
87
|
+
if (implHeader) {
|
|
88
|
+
opts.skipInit = true;
|
|
89
|
+
}
|
|
90
|
+
const planned = (0, architecture_js_1.planOutput)(ast, fileName || "input.cpp", {
|
|
91
|
+
...opts,
|
|
92
|
+
relativeName: opts.relativeName || fileName,
|
|
93
|
+
source: prepared,
|
|
94
|
+
siblingHeader: implHeader,
|
|
95
|
+
});
|
|
96
|
+
if (planned.files) {
|
|
97
|
+
return {
|
|
98
|
+
...planned,
|
|
99
|
+
files: planned.files.map((file) => ({
|
|
100
|
+
...file,
|
|
101
|
+
contents: attachRequires(file.contents, source, ast, opts, file.name),
|
|
102
|
+
})),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const luau = attachRequires((0, emit_js_1.emit)(ast, opts), source, ast, opts, planned.outName || opts.outName || fileName || "input.cpp");
|
|
106
|
+
const name = planned.outName ||
|
|
107
|
+
opts.outName ||
|
|
108
|
+
String(fileName || "input.cpp").replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
109
|
+
return {
|
|
110
|
+
kind: planned.kind || "flat",
|
|
111
|
+
plan: planned.plan,
|
|
112
|
+
files: [{ name, contents: luau }],
|
|
113
|
+
stale: planned.stale || [],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
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.CLANGD_ID = void 0;
|
|
7
|
+
exports.editorBin = editorBin;
|
|
8
|
+
exports.isExtensionInstalled = isExtensionInstalled;
|
|
9
|
+
exports.findClangPlusPlus = findClangPlusPlus;
|
|
10
|
+
exports.ensureLlvm = ensureLlvm;
|
|
11
|
+
exports.installMarketplaceExtension = installMarketplaceExtension;
|
|
12
|
+
exports.reportInstall = reportInstall;
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
15
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
16
|
+
const node_child_process_1 = require("node:child_process");
|
|
17
|
+
exports.CLANGD_ID = "llvm-vs-code-extensions.vscode-clangd";
|
|
18
|
+
function which(command) {
|
|
19
|
+
const finder = process.platform === "win32" ? "where" : "which";
|
|
20
|
+
const result = (0, node_child_process_1.spawnSync)(finder, [command], { encoding: "utf8", windowsHide: true });
|
|
21
|
+
if (result.status !== 0) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return String(result.stdout || "")
|
|
25
|
+
.split(/\r?\n/)
|
|
26
|
+
.map((line) => line.trim())
|
|
27
|
+
.find((line) => line && !line.toLowerCase().includes("info:")) || null;
|
|
28
|
+
}
|
|
29
|
+
function editorBin() {
|
|
30
|
+
const found = which("cursor") || which("code");
|
|
31
|
+
if (found) {
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
const local = process.env.LOCALAPPDATA || "";
|
|
35
|
+
const fallbacks = [
|
|
36
|
+
node_path_1.default.join(local, "Programs", "cursor", "resources", "app", "bin", "cursor.cmd"),
|
|
37
|
+
node_path_1.default.join(local, "Programs", "Microsoft VS Code", "bin", "code.cmd"),
|
|
38
|
+
"/usr/bin/cursor",
|
|
39
|
+
"/usr/local/bin/cursor",
|
|
40
|
+
"/usr/bin/code",
|
|
41
|
+
"/usr/local/bin/code",
|
|
42
|
+
];
|
|
43
|
+
return fallbacks.find((file) => node_fs_1.default.existsSync(file)) || null;
|
|
44
|
+
}
|
|
45
|
+
function extensionHomes() {
|
|
46
|
+
return [node_path_1.default.join(node_os_1.default.homedir(), ".cursor", "extensions"), node_path_1.default.join(node_os_1.default.homedir(), ".vscode", "extensions")];
|
|
47
|
+
}
|
|
48
|
+
function isExtensionInstalled(id) {
|
|
49
|
+
for (const home of extensionHomes()) {
|
|
50
|
+
if (!node_fs_1.default.existsSync(home)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
if (node_fs_1.default.readdirSync(home).some((name) => name.startsWith(`${id}-`))) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const bin = editorBin();
|
|
63
|
+
if (!bin) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const listed = (0, node_child_process_1.spawnSync)(bin, ["--list-extensions"], { encoding: "utf8", windowsHide: true, timeout: 20000 });
|
|
67
|
+
return String(listed.stdout || "").split(/\r?\n/).includes(id);
|
|
68
|
+
}
|
|
69
|
+
function llvmCandidates() {
|
|
70
|
+
if (process.platform === "win32") {
|
|
71
|
+
return [
|
|
72
|
+
"C:/Program Files/LLVM/bin/clang++.exe",
|
|
73
|
+
"C:/Program Files (x86)/LLVM/bin/clang++.exe",
|
|
74
|
+
node_path_1.default.join(process.env.ProgramFiles || "C:/Program Files", "LLVM", "bin", "clang++.exe"),
|
|
75
|
+
"C:/msys64/ucrt64/bin/clang++.exe",
|
|
76
|
+
"C:/msys64/mingw64/bin/clang++.exe",
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
return ["/usr/bin/clang++", "/usr/local/bin/clang++", "/opt/homebrew/bin/clang++"];
|
|
80
|
+
}
|
|
81
|
+
function findClangPlusPlus() {
|
|
82
|
+
for (const candidate of llvmCandidates()) {
|
|
83
|
+
if (candidate && node_fs_1.default.existsSync(candidate)) {
|
|
84
|
+
return candidate.replace(/\\/g, "/");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return which("clang++");
|
|
88
|
+
}
|
|
89
|
+
function ensureLlvm() {
|
|
90
|
+
const found = findClangPlusPlus();
|
|
91
|
+
if (found) {
|
|
92
|
+
return { status: "already", path: found };
|
|
93
|
+
}
|
|
94
|
+
if (process.platform === "win32" && which("winget")) {
|
|
95
|
+
console.log("cluaupp: installing LLVM.LLVM (clang++ / clangd) via winget");
|
|
96
|
+
const result = (0, node_child_process_1.spawnSync)("winget", ["install", "--id", "LLVM.LLVM", "-e", "--accept-package-agreements", "--accept-source-agreements"], { encoding: "utf8", windowsHide: true, timeout: 300000 });
|
|
97
|
+
const after = findClangPlusPlus();
|
|
98
|
+
if (after) {
|
|
99
|
+
return { status: "installed", path: after, how: "winget" };
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
status: "missing",
|
|
103
|
+
detail: String(result.stderr || result.stdout || "").trim() || "winget did not install LLVM",
|
|
104
|
+
hint: "winget install --id LLVM.LLVM -e",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const hint = process.platform === "darwin"
|
|
108
|
+
? "brew install llvm"
|
|
109
|
+
: process.platform === "win32"
|
|
110
|
+
? "winget install --id LLVM.LLVM -e"
|
|
111
|
+
: "sudo apt install clang";
|
|
112
|
+
return { status: "missing", hint };
|
|
113
|
+
}
|
|
114
|
+
function spawnInstall(bin, target) {
|
|
115
|
+
const result = (0, node_child_process_1.spawnSync)(bin, ["--install-extension", target, "--force"], {
|
|
116
|
+
encoding: "utf8",
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
timeout: 180000,
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
ok: result.status === 0 && !/not found/i.test(String(result.stderr || "") + String(result.stdout || "")),
|
|
122
|
+
stdout: String(result.stdout || ""),
|
|
123
|
+
stderr: String(result.stderr || ""),
|
|
124
|
+
status: result.status,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function installMarketplaceExtension(id, label) {
|
|
128
|
+
if (isExtensionInstalled(id)) {
|
|
129
|
+
return { status: "already", id };
|
|
130
|
+
}
|
|
131
|
+
const bin = editorBin();
|
|
132
|
+
if (!bin) {
|
|
133
|
+
return { status: "missing-editor", id };
|
|
134
|
+
}
|
|
135
|
+
console.log("cluaupp: installing", label || id);
|
|
136
|
+
const marketplace = spawnInstall(bin, id);
|
|
137
|
+
if (marketplace.ok) {
|
|
138
|
+
return { status: "installed", id, how: "marketplace" };
|
|
139
|
+
}
|
|
140
|
+
return { status: "failed", id, detail: marketplace.stderr || marketplace.stdout };
|
|
141
|
+
}
|
|
142
|
+
function reportInstall(result, label, extraMissing) {
|
|
143
|
+
if (!result) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const name = label || result.id;
|
|
147
|
+
if (result.status === "already") {
|
|
148
|
+
console.log("cluaupp:", name, "already installed");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (result.status === "installed") {
|
|
152
|
+
console.log("cluaupp: installed", name, result.how ? `(${result.how})` : "");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (result.status === "missing-editor") {
|
|
156
|
+
console.error("cluaupp: Cursor/VS Code CLI not found. Install", name, "from the marketplace.");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (result.status === "missing") {
|
|
160
|
+
console.error("cluaupp: LLVM clang++ not found.", result.hint || extraMissing || "");
|
|
161
|
+
if (result.detail) {
|
|
162
|
+
console.error(" ", result.detail.split(/\r?\n/)[0]);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
console.error("cluaupp: could not install", name + ":", result.detail || "unknown error");
|
|
167
|
+
if (extraMissing) {
|
|
168
|
+
console.error(" ", extraMissing);
|
|
169
|
+
}
|
|
170
|
+
}
|