syncrbx 1.0.0 → 1.1.1
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/README.md +1 -1
- package/cli.js +459 -322
- package/converters.js +60 -11
- package/local-sync.js +631 -441
- package/package.json +26 -20
- package/build.js +0 -77
- package/make_rbxmx.js +0 -21
- package/test.js +0 -28
package/converters.js
CHANGED
|
@@ -68,6 +68,28 @@ function determineScriptType(filename) {
|
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Class a file becomes in Studio, judged by its name alone (the file may
|
|
72
|
+
// already be deleted). Returns ANY_CLASS for .model.json, whose class is inside
|
|
73
|
+
// the file, and null for files SyncRbx does not sync (README.md, meta files...).
|
|
74
|
+
const ANY_CLASS = '*';
|
|
75
|
+
|
|
76
|
+
function classFromFileName(filename) {
|
|
77
|
+
const lower = filename.toLowerCase();
|
|
78
|
+
if (lower.endsWith('.meta.json') || lower === 'meta.json') return null;
|
|
79
|
+
if (lower.endsWith('.model.json')) return ANY_CLASS;
|
|
80
|
+
if (lower.endsWith('.remoteevent')) return 'RemoteEvent';
|
|
81
|
+
if (lower.endsWith('.remotefunction')) return 'RemoteFunction';
|
|
82
|
+
if (lower.endsWith('.bindableevent')) return 'BindableEvent';
|
|
83
|
+
if (lower.endsWith('.bindablefunction')) return 'BindableFunction';
|
|
84
|
+
if (lower.endsWith('.txt')) return 'StringValue';
|
|
85
|
+
if (lower.endsWith('.json')) return 'ModuleScript';
|
|
86
|
+
return determineScriptType(lower);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isInitFile(filename) {
|
|
90
|
+
return filename.startsWith('init.') && (filename.endsWith('.lua') || filename.endsWith('.luau'));
|
|
91
|
+
}
|
|
92
|
+
|
|
71
93
|
// Phase 4 - #7: File checksum
|
|
72
94
|
function fileChecksum(filePath) {
|
|
73
95
|
try {
|
|
@@ -298,7 +320,8 @@ function instanceToFile(instanceData) {
|
|
|
298
320
|
const name = instanceData.name || instanceData.Name || 'Untitled';
|
|
299
321
|
const className = instanceData.className || instanceData.ClassName || 'Folder';
|
|
300
322
|
const source = instanceData.source || (instanceData.Properties && instanceData.Properties.Source) || '';
|
|
301
|
-
|
|
323
|
+
// ?? keeps false and 0, which || would turn into ''.
|
|
324
|
+
const value = instanceData.value ?? (instanceData.Properties && instanceData.Properties.Value) ?? '';
|
|
302
325
|
|
|
303
326
|
if (className === 'Folder') {
|
|
304
327
|
return { fileName: name, content: null, isDirectory: true };
|
|
@@ -359,24 +382,44 @@ function filePathToInstancePath(filePath, projectRoot) {
|
|
|
359
382
|
// ---------------------------------------------------------------------------
|
|
360
383
|
// Phase 2 - #4: Project config loader
|
|
361
384
|
// ---------------------------------------------------------------------------
|
|
385
|
+
const PROJECT_CONFIG_FILE = 'syncrbx.project.json';
|
|
386
|
+
|
|
387
|
+
// Keeps only known Roblox services, without duplicates. Returns null when the
|
|
388
|
+
// value is not a list, meaning "not chosen yet".
|
|
389
|
+
function sanitizeServices(services) {
|
|
390
|
+
if (!Array.isArray(services)) return null;
|
|
391
|
+
return [...new Set(services.filter(s => typeof s === 'string' && ROOT_SERVICES.includes(s)))];
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function readProjectConfigFile(projectRoot) {
|
|
395
|
+
const configPath = path.join(projectRoot, PROJECT_CONFIG_FILE);
|
|
396
|
+
if (!fs.existsSync(configPath)) return {};
|
|
397
|
+
try {
|
|
398
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.error(`[WARN] Error parsing ${PROJECT_CONFIG_FILE}: ${e.message}`);
|
|
401
|
+
return {};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
362
405
|
function loadProjectConfig(projectRoot) {
|
|
363
|
-
const configPath = path.join(projectRoot, 'syncrbx.project.json');
|
|
364
406
|
const defaults = {
|
|
365
407
|
name: path.basename(projectRoot),
|
|
366
408
|
port: 34872,
|
|
367
409
|
ignore: ['*.conflict.bak', 'node_modules', '.git', '.vscode'],
|
|
368
410
|
tree: null, // null = auto-detect from folder names
|
|
411
|
+
services: null, // null = the user has not chosen which services to sync
|
|
369
412
|
};
|
|
413
|
+
const config = { ...defaults, ...readProjectConfigFile(projectRoot) };
|
|
414
|
+
config.services = sanitizeServices(config.services);
|
|
415
|
+
return config;
|
|
416
|
+
}
|
|
370
417
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
console.error(`[WARN] Error parsing syncrbx.project.json: ${e.message}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return defaults;
|
|
418
|
+
// Saves the chosen services, keeping any other settings already in the file.
|
|
419
|
+
function saveProjectServices(projectRoot, services) {
|
|
420
|
+
const config = readProjectConfigFile(projectRoot);
|
|
421
|
+
config.services = services;
|
|
422
|
+
fs.writeFileSync(path.join(projectRoot, PROJECT_CONFIG_FILE), JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
380
423
|
}
|
|
381
424
|
|
|
382
425
|
module.exports = {
|
|
@@ -384,8 +427,11 @@ module.exports = {
|
|
|
384
427
|
STARTER_PLAYER_CHILDREN,
|
|
385
428
|
KNOWN_EXTENSIONS,
|
|
386
429
|
VALUE_CLASSES,
|
|
430
|
+
ANY_CLASS,
|
|
387
431
|
stripExtension,
|
|
388
432
|
determineScriptType,
|
|
433
|
+
classFromFileName,
|
|
434
|
+
isInitFile,
|
|
389
435
|
jsonToLuaTable,
|
|
390
436
|
convertFile,
|
|
391
437
|
processDirectory,
|
|
@@ -394,5 +440,8 @@ module.exports = {
|
|
|
394
440
|
filePathToInstancePath,
|
|
395
441
|
getMetaProperties,
|
|
396
442
|
loadProjectConfig,
|
|
443
|
+
saveProjectServices,
|
|
444
|
+
sanitizeServices,
|
|
445
|
+
PROJECT_CONFIG_FILE,
|
|
397
446
|
fileChecksum,
|
|
398
447
|
};
|