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/src/cli.js
DELETED
|
@@ -1,481 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { compileService } = require("./compile");
|
|
6
|
-
const { isSourceFile, isHeaderFile, toLuauPath, siblingImplementation } = require("./preprocess");
|
|
7
|
-
const { syncEditorSupport, installEditorSupport } = require("./intellisense");
|
|
8
|
-
const pkg = require("../package.json");
|
|
9
|
-
|
|
10
|
-
function printHelp() {
|
|
11
|
-
console.log(`Cluaupp ${pkg.version} — C++ × Luau
|
|
12
|
-
|
|
13
|
-
Usage:
|
|
14
|
-
cluaupp init [folder] create a game (src/server, src/client, src/shared)
|
|
15
|
-
cluaupp build [folder] transpile src → out (PascalCase services)
|
|
16
|
-
cluaupp watch [folder] rebuild on save
|
|
17
|
-
cluaupp lsp [folder] language server (stdio JSON-RPC)
|
|
18
|
-
cluaupp intellisense install Cursor/VS Code IntelliSense
|
|
19
|
-
cluaupp --version print version
|
|
20
|
-
cluaupp --help this help
|
|
21
|
-
|
|
22
|
-
Flow: cluaupp init → cluaupp build → rojo serve (plugin in Roblox Studio)
|
|
23
|
-
Docs: https://github.com/kartzDev/cluaupp
|
|
24
|
-
`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function copyDir(from, to) {
|
|
28
|
-
fs.mkdirSync(to, { recursive: true });
|
|
29
|
-
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
30
|
-
const src = path.join(from, entry.name);
|
|
31
|
-
const dest = path.join(to, entry.name);
|
|
32
|
-
if (entry.isDirectory()) {
|
|
33
|
-
copyDir(src, dest);
|
|
34
|
-
} else {
|
|
35
|
-
fs.copyFileSync(src, dest);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function tryRm(target) {
|
|
41
|
-
try {
|
|
42
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
43
|
-
return true;
|
|
44
|
-
} catch (err) {
|
|
45
|
-
if (err.code === "EPERM" || err.code === "EBUSY" || err.code === "ENOTEMPTY") {
|
|
46
|
-
console.error("cluaupp: skip remove", target, `(${err.code})`);
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
throw err;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function copyFileIfChanged(src, dest, mode = "fill") {
|
|
54
|
-
if (mode === "fill" && fs.existsSync(dest)) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (mode === "update" && fs.existsSync(dest)) {
|
|
58
|
-
const from = fs.statSync(src);
|
|
59
|
-
const to = fs.statSync(dest);
|
|
60
|
-
if (from.size === to.size && from.mtimeMs <= to.mtimeMs) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
65
|
-
try {
|
|
66
|
-
fs.copyFileSync(src, dest);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
if (err.code === "EPERM" || err.code === "EBUSY") {
|
|
69
|
-
console.error("cluaupp: skip copy", dest, `(${err.code})`);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
throw err;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function syncDir(from, to, mode = "fill") {
|
|
77
|
-
if (!fs.existsSync(from)) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
fs.mkdirSync(to, { recursive: true });
|
|
81
|
-
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
82
|
-
const src = path.join(from, entry.name);
|
|
83
|
-
const dest = path.join(to, entry.name);
|
|
84
|
-
if (entry.isDirectory()) {
|
|
85
|
-
syncDir(src, dest, mode);
|
|
86
|
-
} else {
|
|
87
|
-
copyFileIfChanged(src, dest, mode);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function copyRuntime(dest) {
|
|
93
|
-
const runtime = path.join(__dirname, "..", "runtime");
|
|
94
|
-
if (!fs.existsSync(runtime)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
syncDir(runtime, path.join(dest, "libs"));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function copyHeaders(dest) {
|
|
101
|
-
const from = path.join(__dirname, "..", "include", "cluaupp");
|
|
102
|
-
if (!fs.existsSync(from)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
syncDir(from, path.join(dest, "include", "cluaupp"));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function loadConfig(root) {
|
|
109
|
-
const configNames = ["cluaupp.config.json", "cluau.config.json"];
|
|
110
|
-
for (const name of configNames) {
|
|
111
|
-
const file = path.join(root, name);
|
|
112
|
-
if (fs.existsSync(file)) {
|
|
113
|
-
return { rootDir: "src", outDir: "out", strict: true, ...JSON.parse(fs.readFileSync(file, "utf8")) };
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return { rootDir: "src", outDir: "out", strict: true };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function collectCpp(dir, files = []) {
|
|
120
|
-
if (!fs.existsSync(dir)) {
|
|
121
|
-
return files;
|
|
122
|
-
}
|
|
123
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
124
|
-
const full = path.join(dir, entry.name);
|
|
125
|
-
if (entry.isDirectory()) {
|
|
126
|
-
collectCpp(full, files);
|
|
127
|
-
} else if (isSourceFile(entry.name)) {
|
|
128
|
-
files.push(full);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return files;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function collectFiles(dir, files = []) {
|
|
135
|
-
if (!fs.existsSync(dir)) {
|
|
136
|
-
return files;
|
|
137
|
-
}
|
|
138
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
139
|
-
const full = path.join(dir, entry.name);
|
|
140
|
-
if (entry.isDirectory()) {
|
|
141
|
-
collectFiles(full, files);
|
|
142
|
-
} else {
|
|
143
|
-
files.push(full);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return files;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function siblingCpp(file) {
|
|
150
|
-
return siblingImplementation(file) || file.replace(/\.(h|hpp|hh)$/i, ".cpp");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function resolveKey(file) {
|
|
154
|
-
const resolved = path.resolve(file);
|
|
155
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function posixRel(from, file) {
|
|
159
|
-
return path.relative(from, file).replace(/\\/g, "/");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function sourcePrefixes(rel) {
|
|
163
|
-
const noExt = rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, "");
|
|
164
|
-
const noTag = noExt.replace(/\.(server|client)$/i, "");
|
|
165
|
-
return [...new Set([noExt, noTag, toLuauPath(rel).replace(/\\/g, "/")])];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function matchesPrefix(relOut, prefixes) {
|
|
169
|
-
const n = relOut.replace(/\\/g, "/").toLowerCase();
|
|
170
|
-
return prefixes.some((prefix) => {
|
|
171
|
-
const k = prefix.replace(/\\/g, "/").toLowerCase();
|
|
172
|
-
return n === k || n === `${k}.luau` || n.startsWith(`${k}/`) || n.startsWith(`${k}.`);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function removeEmptyOutDirs(outDir, projectRoot) {
|
|
177
|
-
if (!fs.existsSync(outDir)) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const walk = (dir) => {
|
|
181
|
-
if (resolveKey(dir) === resolveKey(outDir)) {
|
|
182
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
183
|
-
if (entry.isDirectory()) {
|
|
184
|
-
walk(path.join(dir, entry.name));
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
190
|
-
if (entry.isDirectory()) {
|
|
191
|
-
walk(path.join(dir, entry.name));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (fs.existsSync(dir) && fs.readdirSync(dir).length === 0) {
|
|
195
|
-
if (tryRm(dir)) {
|
|
196
|
-
console.log("cluaupp: removed", path.relative(projectRoot, dir));
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
walk(outDir);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function pruneOut(root, config, written, failedPrefixes) {
|
|
204
|
-
const outDir = path.join(root, config.outDir);
|
|
205
|
-
if (!fs.existsSync(outDir)) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
for (const file of collectFiles(outDir)) {
|
|
209
|
-
if (written.has(resolveKey(file))) {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const rel = posixRel(outDir, file);
|
|
213
|
-
if (matchesPrefix(rel, failedPrefixes)) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (tryRm(file)) {
|
|
217
|
-
console.log("cluaupp: removed", path.relative(root, file));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
removeEmptyOutDirs(outDir, root);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function compileProject(root, config) {
|
|
224
|
-
const srcDir = path.join(root, config.rootDir);
|
|
225
|
-
const files = collectCpp(srcDir);
|
|
226
|
-
const fileSet = new Set(files.map((file) => path.resolve(file)));
|
|
227
|
-
const jobs = [];
|
|
228
|
-
const errors = [];
|
|
229
|
-
|
|
230
|
-
for (const file of files) {
|
|
231
|
-
if (isHeaderFile(file) && fileSet.has(path.resolve(siblingCpp(file)))) {
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
const source = fs.readFileSync(file, "utf8");
|
|
235
|
-
const rel = posixRel(srcDir, file);
|
|
236
|
-
try {
|
|
237
|
-
const result = compileService(source, rel, {
|
|
238
|
-
...config,
|
|
239
|
-
filePath: file,
|
|
240
|
-
relativeName: rel,
|
|
241
|
-
outName: toLuauPath(rel),
|
|
242
|
-
architecture: config.architecture !== false,
|
|
243
|
-
includeDirs: [path.dirname(file), srcDir, path.join(root, "include")],
|
|
244
|
-
srcDir,
|
|
245
|
-
});
|
|
246
|
-
jobs.push({ rel, files: result.files, stale: result.stale || [] });
|
|
247
|
-
} catch (err) {
|
|
248
|
-
errors.push({ rel, prefixes: sourcePrefixes(rel), message: err.message });
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return { files, jobs, errors };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function writeTextIfChanged(dest, contents) {
|
|
256
|
-
if (fs.existsSync(dest) && fs.readFileSync(dest, "utf8") === contents) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
260
|
-
fs.writeFileSync(dest, contents, "utf8");
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function writeJobs(root, config, jobs) {
|
|
265
|
-
const written = new Set();
|
|
266
|
-
for (const job of jobs) {
|
|
267
|
-
for (const artifact of job.files) {
|
|
268
|
-
const dest = path.join(root, config.outDir, artifact.name);
|
|
269
|
-
const changed = writeTextIfChanged(dest, artifact.contents);
|
|
270
|
-
written.add(resolveKey(dest));
|
|
271
|
-
if (changed) {
|
|
272
|
-
console.log("cluaupp:", job.rel, "→", path.relative(root, dest));
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
for (const stale of job.stale) {
|
|
276
|
-
const dest = path.join(root, config.outDir, stale);
|
|
277
|
-
if (fs.existsSync(dest) && !written.has(resolveKey(dest))) {
|
|
278
|
-
if (tryRm(dest)) {
|
|
279
|
-
console.log("cluaupp: removed", path.relative(root, dest));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return written;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function libsPresent(root) {
|
|
288
|
-
const libs = path.join(root, "libs");
|
|
289
|
-
return fs.existsSync(libs) && fs.readdirSync(libs).length > 0;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function ensureVendor(root) {
|
|
293
|
-
copyRuntime(root);
|
|
294
|
-
copyHeaders(root);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function build(root, options = {}) {
|
|
298
|
-
const exitOnError = options.exitOnError !== false;
|
|
299
|
-
const holdOnError = options.holdOnError === true;
|
|
300
|
-
const config = loadConfig(root);
|
|
301
|
-
if (options.syncVendor !== false) {
|
|
302
|
-
ensureVendor(root);
|
|
303
|
-
}
|
|
304
|
-
const srcDir = path.join(root, config.rootDir);
|
|
305
|
-
if (!fs.existsSync(srcDir) || collectCpp(srcDir).length === 0) {
|
|
306
|
-
console.error("no .cpp/.h/.hpp files in", config.rootDir);
|
|
307
|
-
if (!holdOnError) {
|
|
308
|
-
pruneOut(root, config, new Set(), []);
|
|
309
|
-
}
|
|
310
|
-
if (exitOnError) {
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
return { failed: 0, written: new Set() };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const compiled = compileProject(root, config);
|
|
317
|
-
if (compiled.errors.length > 0) {
|
|
318
|
-
for (const err of compiled.errors) {
|
|
319
|
-
console.error(err.message);
|
|
320
|
-
}
|
|
321
|
-
if (holdOnError) {
|
|
322
|
-
console.error(`cluaupp: out not updated (${compiled.errors.length} compile error${compiled.errors.length === 1 ? "" : "s"})`);
|
|
323
|
-
try {
|
|
324
|
-
syncEditorSupport(root, config);
|
|
325
|
-
} catch (err) {
|
|
326
|
-
console.error("cluaupp: intellisense sync failed", err.message);
|
|
327
|
-
}
|
|
328
|
-
return { failed: compiled.errors.length, written: new Set() };
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const written = writeJobs(root, config, compiled.jobs);
|
|
333
|
-
const failedPrefixes = compiled.errors.flatMap((err) => err.prefixes);
|
|
334
|
-
pruneOut(root, config, written, failedPrefixes);
|
|
335
|
-
try {
|
|
336
|
-
syncEditorSupport(root, config);
|
|
337
|
-
} catch (err) {
|
|
338
|
-
console.error("cluaupp: intellisense sync failed", err.message);
|
|
339
|
-
}
|
|
340
|
-
if (compiled.errors.length > 0 && exitOnError) {
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
return { failed: compiled.errors.length, written };
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
async function init(dest) {
|
|
347
|
-
const template = path.join(__dirname, "..", "templates", "game");
|
|
348
|
-
const include = path.join(__dirname, "..", "include");
|
|
349
|
-
copyDir(template, dest);
|
|
350
|
-
copyDir(include, path.join(dest, "include"));
|
|
351
|
-
copyRuntime(dest);
|
|
352
|
-
syncEditorSupport(dest);
|
|
353
|
-
const installed = await installEditorSupport();
|
|
354
|
-
console.log("Cluaupp ready in", dest);
|
|
355
|
-
console.log(" cluaupp build");
|
|
356
|
-
console.log(" rojo serve");
|
|
357
|
-
if (installed.local.length || (installed.cpp && installed.cpp.status === "installed")) {
|
|
358
|
-
console.log(" reload Cursor (Ctrl+Shift+P → Developer: Reload Window)");
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function pidAlive(pid) {
|
|
363
|
-
try {
|
|
364
|
-
process.kill(pid, 0);
|
|
365
|
-
return true;
|
|
366
|
-
} catch {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function acquireWatchLock(root) {
|
|
372
|
-
const file = path.join(root, ".cluaupp-watch.lock");
|
|
373
|
-
if (fs.existsSync(file)) {
|
|
374
|
-
const pid = Number(fs.readFileSync(file, "utf8").trim());
|
|
375
|
-
if (Number.isFinite(pid) && pid !== process.pid && pidAlive(pid)) {
|
|
376
|
-
console.error(`cluaupp: watch already running (pid ${pid}). Stop it first — two watchers make Rojo crash on libs/.`);
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
fs.writeFileSync(file, String(process.pid), "utf8");
|
|
381
|
-
const release = () => {
|
|
382
|
-
try {
|
|
383
|
-
if (fs.existsSync(file) && fs.readFileSync(file, "utf8").trim() === String(process.pid)) {
|
|
384
|
-
fs.unlinkSync(file);
|
|
385
|
-
}
|
|
386
|
-
} catch {
|
|
387
|
-
// ignore
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
process.on("exit", release);
|
|
391
|
-
process.on("SIGINT", () => {
|
|
392
|
-
release();
|
|
393
|
-
process.exit(0);
|
|
394
|
-
});
|
|
395
|
-
process.on("SIGTERM", () => {
|
|
396
|
-
release();
|
|
397
|
-
process.exit(0);
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function watch(root) {
|
|
402
|
-
acquireWatchLock(root);
|
|
403
|
-
build(root, { exitOnError: false, syncVendor: false, holdOnError: true });
|
|
404
|
-
const config = loadConfig(root);
|
|
405
|
-
const dir = path.join(root, config.rootDir);
|
|
406
|
-
let timer = null;
|
|
407
|
-
let running = false;
|
|
408
|
-
let queued = false;
|
|
409
|
-
|
|
410
|
-
const run = () => {
|
|
411
|
-
if (running) {
|
|
412
|
-
queued = true;
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
running = true;
|
|
416
|
-
try {
|
|
417
|
-
build(root, { exitOnError: false, syncVendor: false, holdOnError: true });
|
|
418
|
-
} catch (err) {
|
|
419
|
-
console.error(err.message);
|
|
420
|
-
} finally {
|
|
421
|
-
running = false;
|
|
422
|
-
if (queued) {
|
|
423
|
-
queued = false;
|
|
424
|
-
run();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
console.log("cluaupp", pkg.version, "watching", dir, "(libs untouched)");
|
|
430
|
-
if (!fs.existsSync(dir)) {
|
|
431
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
432
|
-
}
|
|
433
|
-
fs.watch(dir, { recursive: true }, () => {
|
|
434
|
-
clearTimeout(timer);
|
|
435
|
-
timer = setTimeout(run, 400);
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function dispatch(args) {
|
|
440
|
-
const cmd = args[0] || "help";
|
|
441
|
-
const cwd = process.cwd();
|
|
442
|
-
if (cmd === "init") {
|
|
443
|
-
return init(path.resolve(cwd, args[1] || ".")).catch((err) => {
|
|
444
|
-
console.error(err.message || err);
|
|
445
|
-
process.exit(1);
|
|
446
|
-
});
|
|
447
|
-
} else if (cmd === "build") {
|
|
448
|
-
build(path.resolve(cwd, args[1] || "."));
|
|
449
|
-
} else if (cmd === "watch") {
|
|
450
|
-
watch(path.resolve(cwd, args[1] || "."));
|
|
451
|
-
} else if (cmd === "lsp") {
|
|
452
|
-
const { start } = require("./lsp");
|
|
453
|
-
start({ projectRoot: path.resolve(cwd, args[1] || ".") });
|
|
454
|
-
} else if (cmd === "intellisense" || cmd === "intelisense") {
|
|
455
|
-
const root = path.resolve(cwd, args[1] || ".");
|
|
456
|
-
syncEditorSupport(root, loadConfig(root));
|
|
457
|
-
return installEditorSupport().then((installed) => {
|
|
458
|
-
console.log("cluaupp: compile_commands.json, .clangd, and .vscode updated in", root);
|
|
459
|
-
if (installed.local.length) {
|
|
460
|
-
for (const dest of installed.local) {
|
|
461
|
-
console.log("cluaupp: Cluaupp IntelliSense", dest);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}).catch((err) => {
|
|
465
|
-
console.error(err.message || err);
|
|
466
|
-
process.exit(1);
|
|
467
|
-
});
|
|
468
|
-
} else if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
469
|
-
console.log(pkg.version);
|
|
470
|
-
} else {
|
|
471
|
-
printHelp();
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const launchedAsCli =
|
|
476
|
-
require.main && ["cli.js", "cluaupp.js", "cluau.js"].includes(path.basename(require.main.filename));
|
|
477
|
-
if (launchedAsCli) {
|
|
478
|
-
dispatch(process.argv.slice(2));
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
module.exports = { build, watch, dispatch };
|
package/src/compile.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { parse } = require("./parse");
|
|
4
|
-
const { emit } = require("./emit");
|
|
5
|
-
const { preprocess } = require("./preprocess");
|
|
6
|
-
const { collectLibraries, insertRequires, insertModuleRequires } = require("./libs");
|
|
7
|
-
const { planOutput } = require("./architecture");
|
|
8
|
-
|
|
9
|
-
function attachRequires(contents, source, ast, options, outName) {
|
|
10
|
-
let next = contents;
|
|
11
|
-
if (!next.includes("CluauppLibs")) {
|
|
12
|
-
next = insertRequires(next, collectLibraries(source, ast));
|
|
13
|
-
}
|
|
14
|
-
return insertModuleRequires(next, options.moduleIncludes || [], outName);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function compileSource(source, fileName, options = {}) {
|
|
18
|
-
options.moduleIncludes = options.moduleIncludes || [];
|
|
19
|
-
const prepared = options.filePath ? preprocess(source, options.filePath, options) : source;
|
|
20
|
-
const ast = parse(prepared, fileName || "input.cpp");
|
|
21
|
-
const luau = emit(ast, options);
|
|
22
|
-
return attachRequires(luau, source, ast, options, options.outName || fileName);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function compileService(source, fileName, options = {}) {
|
|
26
|
-
options.moduleIncludes = options.moduleIncludes || [];
|
|
27
|
-
const prepared = options.filePath ? preprocess(source, options.filePath, options) : source;
|
|
28
|
-
const ast = parse(prepared, fileName || "input.cpp");
|
|
29
|
-
const planned = planOutput(ast, fileName || "input.cpp", {
|
|
30
|
-
...options,
|
|
31
|
-
relativeName: options.relativeName || fileName,
|
|
32
|
-
source: prepared,
|
|
33
|
-
});
|
|
34
|
-
if (planned.files) {
|
|
35
|
-
return {
|
|
36
|
-
...planned,
|
|
37
|
-
files: planned.files.map((file) => ({
|
|
38
|
-
...file,
|
|
39
|
-
contents: attachRequires(file.contents, source, ast, options, file.name),
|
|
40
|
-
})),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
const luau = attachRequires(emit(ast, options), source, ast, options, planned.outName || options.outName || fileName);
|
|
44
|
-
const name =
|
|
45
|
-
planned.outName ||
|
|
46
|
-
options.outName ||
|
|
47
|
-
String(fileName || "input.cpp").replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
48
|
-
return {
|
|
49
|
-
kind: planned.kind || "flat",
|
|
50
|
-
plan: planned.plan,
|
|
51
|
-
files: [{ name, contents: luau }],
|
|
52
|
-
stale: planned.stale || [],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = { compileSource, compileService };
|