cluaupp 0.1.1 → 0.1.2
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 +8 -1
- package/docs/cli.md +1 -1
- package/package.json +2 -2
- package/src/cli.js +119 -60
- package/templates/game/default.project.json +5 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
- Watch does not write `out/` while any file fails to compile (Studio keeps the last good scripts).
|
|
6
|
+
- Watch/build skip rewriting Luau whose contents did not change (a space in C++ no longer floods Rojo).
|
|
7
|
+
- `libs/` is fill-only: missing files are restored, existing files are never overwritten or deleted. Fixes Rojo 7 crashing on `libs/ArrayIndexer` when watch/build raced a live serve.
|
|
8
|
+
- Dropped `$optional` from `default.project.json` (Rojo 7.7 failed to deserialize it).
|
|
9
|
+
|
|
3
10
|
## 0.1.1
|
|
4
11
|
|
|
5
12
|
- DataService: `DataServiceOptions<T>.Template` is the required player-data table (`T` matches the save struct). Also documents `Exclude`.
|
|
6
13
|
- Compiler: C++ designated initializers (`Type { .Field = value }`) emit Luau tables, so `DataService.Server:Init({ Template = ... })` type-checks and compiles.
|
|
7
|
-
- Watch/build: delete orphaned `out/` files when a source is removed. Never wipe `out/` or `libs/` as a whole — Rojo 7 crashes if `libs/ArrayIndexer` disappears mid-serve. Vendor
|
|
14
|
+
- Watch/build: delete orphaned `out/` files when a source is removed. Never wipe `out/` or `libs/` as a whole — Rojo 7 crashes if `libs/ArrayIndexer` disappears mid-serve. Vendor files are only created if missing; existing `libs/` is never overwritten on watch/build. Watch skips writing `out/` when compile fails, and skips rewriting Luau whose contents did not change.
|
|
8
15
|
|
|
9
16
|
## 0.1.0
|
|
10
17
|
|
package/docs/cli.md
CHANGED
|
@@ -41,7 +41,7 @@ cluaupp build ./my-game
|
|
|
41
41
|
|
|
42
42
|
## `cluaupp watch [folder]`
|
|
43
43
|
|
|
44
|
-
Runs a `build` and rebuilds when anything under `src/` changes, including deletes. Parse errors are printed; the watcher stays alive. Rebuilds are debounced and do **not** recopy `libs
|
|
44
|
+
Runs a `build` and rebuilds when anything under `src/` changes, including deletes. Parse errors are printed; the watcher stays alive and **does not write `out/`** until the project compiles cleanly (Studio keeps the last good scripts). Rebuilds are debounced and do **not** recopy `libs/`. Deleted `.cpp` files prune their `out/` artifacts on the next successful compile.
|
|
45
45
|
|
|
46
46
|
## `cluaupp --version` / `cluaupp -v`
|
|
47
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cluaupp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Cluaupp — the definitive merge of C++ and modern Luau. Source-to-source transpiler with first-class Roblox APIs.",
|
|
5
5
|
"author": "KartzDev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"cluau": "node bin/cluaupp.js",
|
|
30
30
|
"build": "node bin/cluaupp.js build",
|
|
31
31
|
"watch": "node bin/cluaupp.js watch",
|
|
32
|
-
"test": "node test/leaderstats.test.js && node test/understand.test.js && node test/datatypes.test.js && node test/headers.test.js && node test/libs.test.js && node test/runtime-libs.test.js && node test/intellisense.test.js && node test/dataservice.test.js && node test/prune.test.js",
|
|
32
|
+
"test": "node test/leaderstats.test.js && node test/understand.test.js && node test/datatypes.test.js && node test/headers.test.js && node test/libs.test.js && node test/runtime-libs.test.js && node test/intellisense.test.js && node test/dataservice.test.js && node test/prune.test.js && node test/hold.test.js",
|
|
33
33
|
"generate-api": "node scripts/generate-api.js",
|
|
34
34
|
"check-highlight": "node scripts/check-highlight.js",
|
|
35
35
|
"vendor-libs": "node scripts/vendor-libs.js",
|
package/src/cli.js
CHANGED
|
@@ -47,8 +47,11 @@ function tryRm(target) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function copyFileIfChanged(src, dest) {
|
|
51
|
-
if (fs.existsSync(dest)) {
|
|
50
|
+
function copyFileIfChanged(src, dest, mode = "fill") {
|
|
51
|
+
if (mode === "fill" && fs.existsSync(dest)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (mode === "update" && fs.existsSync(dest)) {
|
|
52
55
|
const from = fs.statSync(src);
|
|
53
56
|
const to = fs.statSync(dest);
|
|
54
57
|
if (from.size === to.size && from.mtimeMs <= to.mtimeMs) {
|
|
@@ -67,7 +70,7 @@ function copyFileIfChanged(src, dest) {
|
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
function syncDir(from, to) {
|
|
73
|
+
function syncDir(from, to, mode = "fill") {
|
|
71
74
|
if (!fs.existsSync(from)) {
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
@@ -76,9 +79,9 @@ function syncDir(from, to) {
|
|
|
76
79
|
const src = path.join(from, entry.name);
|
|
77
80
|
const dest = path.join(to, entry.name);
|
|
78
81
|
if (entry.isDirectory()) {
|
|
79
|
-
syncDir(src, dest);
|
|
82
|
+
syncDir(src, dest, mode);
|
|
80
83
|
} else {
|
|
81
|
-
copyFileIfChanged(src, dest);
|
|
84
|
+
copyFileIfChanged(src, dest, mode);
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
}
|
|
@@ -214,29 +217,13 @@ function pruneOut(root, config, written, failedPrefixes) {
|
|
|
214
217
|
removeEmptyOutDirs(outDir, root);
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
function
|
|
218
|
-
const exitOnError = options.exitOnError !== false;
|
|
219
|
-
const syncVendor = options.syncVendor !== false;
|
|
220
|
-
const config = loadConfig(root);
|
|
221
|
-
if (syncVendor) {
|
|
222
|
-
copyRuntime(root);
|
|
223
|
-
copyHeaders(root);
|
|
224
|
-
}
|
|
220
|
+
function compileProject(root, config) {
|
|
225
221
|
const srcDir = path.join(root, config.rootDir);
|
|
226
222
|
const files = collectCpp(srcDir);
|
|
227
|
-
if (files.length === 0) {
|
|
228
|
-
console.error("no .cpp/.h/.hpp files in", config.rootDir);
|
|
229
|
-
pruneOut(root, config, new Set(), []);
|
|
230
|
-
if (exitOnError) {
|
|
231
|
-
process.exit(1);
|
|
232
|
-
}
|
|
233
|
-
return { failed: 0, written: new Set() };
|
|
234
|
-
}
|
|
235
|
-
|
|
236
223
|
const fileSet = new Set(files.map((file) => path.resolve(file)));
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
224
|
+
const jobs = [];
|
|
225
|
+
const errors = [];
|
|
226
|
+
|
|
240
227
|
for (const file of files) {
|
|
241
228
|
if (isHeaderFile(file) && fileSet.has(path.resolve(siblingCpp(file)))) {
|
|
242
229
|
continue;
|
|
@@ -252,32 +239,98 @@ function build(root, options = {}) {
|
|
|
252
239
|
architecture: config.architecture !== false,
|
|
253
240
|
includeDirs: [path.dirname(file), srcDir, path.join(root, "include")],
|
|
254
241
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
242
|
+
jobs.push({ rel, files: result.files, stale: result.stale || [] });
|
|
243
|
+
} catch (err) {
|
|
244
|
+
errors.push({ rel, prefixes: sourcePrefixes(rel), message: err.message });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return { files, jobs, errors };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function writeTextIfChanged(dest, contents) {
|
|
252
|
+
if (fs.existsSync(dest) && fs.readFileSync(dest, "utf8") === contents) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
256
|
+
fs.writeFileSync(dest, contents, "utf8");
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function writeJobs(root, config, jobs) {
|
|
261
|
+
const written = new Set();
|
|
262
|
+
for (const job of jobs) {
|
|
263
|
+
for (const artifact of job.files) {
|
|
264
|
+
const dest = path.join(root, config.outDir, artifact.name);
|
|
265
|
+
const changed = writeTextIfChanged(dest, artifact.contents);
|
|
266
|
+
written.add(resolveKey(dest));
|
|
267
|
+
if (changed) {
|
|
268
|
+
console.log("cluaupp:", job.rel, "→", path.relative(root, dest));
|
|
261
269
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
}
|
|
271
|
+
for (const stale of job.stale) {
|
|
272
|
+
const dest = path.join(root, config.outDir, stale);
|
|
273
|
+
if (fs.existsSync(dest) && !written.has(resolveKey(dest))) {
|
|
274
|
+
if (tryRm(dest)) {
|
|
275
|
+
console.log("cluaupp: removed", path.relative(root, dest));
|
|
268
276
|
}
|
|
269
277
|
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return written;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function libsPresent(root) {
|
|
284
|
+
const libs = path.join(root, "libs");
|
|
285
|
+
return fs.existsSync(libs) && fs.readdirSync(libs).length > 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function ensureVendor(root) {
|
|
289
|
+
copyRuntime(root);
|
|
290
|
+
copyHeaders(root);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function build(root, options = {}) {
|
|
294
|
+
const exitOnError = options.exitOnError !== false;
|
|
295
|
+
const holdOnError = options.holdOnError === true;
|
|
296
|
+
const syncVendor = options.syncVendor === true;
|
|
297
|
+
const config = loadConfig(root);
|
|
298
|
+
if (syncVendor) {
|
|
299
|
+
copyRuntime(root);
|
|
300
|
+
copyHeaders(root);
|
|
301
|
+
} else {
|
|
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) {
|
|
273
319
|
console.error(err.message);
|
|
274
320
|
}
|
|
321
|
+
if (holdOnError) {
|
|
322
|
+
console.error(`cluaupp: out not updated (${compiled.errors.length} compile error${compiled.errors.length === 1 ? "" : "s"})`);
|
|
323
|
+
return { failed: compiled.errors.length, written: new Set() };
|
|
324
|
+
}
|
|
275
325
|
}
|
|
326
|
+
|
|
327
|
+
const written = writeJobs(root, config, compiled.jobs);
|
|
328
|
+
const failedPrefixes = compiled.errors.flatMap((err) => err.prefixes);
|
|
276
329
|
pruneOut(root, config, written, failedPrefixes);
|
|
277
|
-
if (
|
|
330
|
+
if (compiled.errors.length > 0 && exitOnError) {
|
|
278
331
|
process.exit(1);
|
|
279
332
|
}
|
|
280
|
-
return { failed, written };
|
|
333
|
+
return { failed: compiled.errors.length, written };
|
|
281
334
|
}
|
|
282
335
|
|
|
283
336
|
function init(dest) {
|
|
@@ -292,7 +345,7 @@ function init(dest) {
|
|
|
292
345
|
}
|
|
293
346
|
|
|
294
347
|
function watch(root) {
|
|
295
|
-
build(root, { exitOnError: false, syncVendor: true });
|
|
348
|
+
build(root, { exitOnError: false, syncVendor: false, holdOnError: true });
|
|
296
349
|
const config = loadConfig(root);
|
|
297
350
|
const dir = path.join(root, config.rootDir);
|
|
298
351
|
let timer = null;
|
|
@@ -306,7 +359,7 @@ function watch(root) {
|
|
|
306
359
|
}
|
|
307
360
|
running = true;
|
|
308
361
|
try {
|
|
309
|
-
build(root, { exitOnError: false, syncVendor: false });
|
|
362
|
+
build(root, { exitOnError: false, syncVendor: false, holdOnError: true });
|
|
310
363
|
} catch (err) {
|
|
311
364
|
console.error(err.message);
|
|
312
365
|
} finally {
|
|
@@ -324,24 +377,30 @@ function watch(root) {
|
|
|
324
377
|
}
|
|
325
378
|
fs.watch(dir, { recursive: true }, () => {
|
|
326
379
|
clearTimeout(timer);
|
|
327
|
-
timer = setTimeout(run,
|
|
380
|
+
timer = setTimeout(run, 400);
|
|
328
381
|
});
|
|
329
382
|
}
|
|
330
383
|
|
|
331
|
-
|
|
332
|
-
const cmd = args[0] || "help";
|
|
333
|
-
const cwd = process.cwd();
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
384
|
+
function dispatch(args) {
|
|
385
|
+
const cmd = args[0] || "help";
|
|
386
|
+
const cwd = process.cwd();
|
|
387
|
+
if (cmd === "init") {
|
|
388
|
+
init(path.resolve(cwd, args[1] || "."));
|
|
389
|
+
} else if (cmd === "build") {
|
|
390
|
+
build(path.resolve(cwd, args[1] || "."));
|
|
391
|
+
} else if (cmd === "watch") {
|
|
392
|
+
watch(path.resolve(cwd, args[1] || "."));
|
|
393
|
+
} else if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
394
|
+
console.log(pkg.version);
|
|
395
|
+
} else {
|
|
396
|
+
printHelp();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const launchedAsCli =
|
|
401
|
+
require.main && ["cli.js", "cluaupp.js", "cluau.js"].includes(path.basename(require.main.filename));
|
|
402
|
+
if (launchedAsCli) {
|
|
403
|
+
dispatch(process.argv.slice(2));
|
|
345
404
|
}
|
|
346
405
|
|
|
347
|
-
module.exports = { build, watch };
|
|
406
|
+
module.exports = { build, watch, dispatch };
|
|
@@ -5,23 +5,19 @@
|
|
|
5
5
|
"ServerScriptService": {
|
|
6
6
|
"$className": "ServerScriptService",
|
|
7
7
|
"Cluaupp": {
|
|
8
|
-
"$path": "out/server"
|
|
9
|
-
"$optional": true
|
|
8
|
+
"$path": "out/server"
|
|
10
9
|
}
|
|
11
10
|
},
|
|
12
11
|
"ReplicatedStorage": {
|
|
13
12
|
"$className": "ReplicatedStorage",
|
|
14
13
|
"Cluaupp": {
|
|
15
|
-
"$path": "out/shared"
|
|
16
|
-
"$optional": true
|
|
14
|
+
"$path": "out/shared"
|
|
17
15
|
},
|
|
18
16
|
"CluauppLibs": {
|
|
19
|
-
"$path": "libs"
|
|
20
|
-
"$optional": true
|
|
17
|
+
"$path": "libs"
|
|
21
18
|
},
|
|
22
19
|
"Packages": {
|
|
23
|
-
"$path": "Packages"
|
|
24
|
-
"$optional": true
|
|
20
|
+
"$path": "Packages"
|
|
25
21
|
}
|
|
26
22
|
},
|
|
27
23
|
"StarterPlayer": {
|
|
@@ -29,8 +25,7 @@
|
|
|
29
25
|
"StarterPlayerScripts": {
|
|
30
26
|
"$className": "StarterPlayerScripts",
|
|
31
27
|
"Cluaupp": {
|
|
32
|
-
"$path": "out/client"
|
|
33
|
-
"$optional": true
|
|
28
|
+
"$path": "out/client"
|
|
34
29
|
}
|
|
35
30
|
}
|
|
36
31
|
},
|