msfs-layout-generator 0.2.1 → 0.3.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/README.MD +5 -1
- package/dist/cli.js +153 -4
- package/package.json +2 -1
package/README.MD
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
   
|
|
5
5
|
|
|
6
6
|
A tool for **Microsoft Flight Simulator (MSFS)** developers to automatically generate and update `layout.json` files for their add-ons.
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+

|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
## ✨ Features
|
|
@@ -12,6 +13,9 @@ A tool for **Microsoft Flight Simulator (MSFS)** developers to automatically gen
|
|
|
12
13
|
- **Automatic Layout Generation**
|
|
13
14
|
Scans package directories and creates `layout.json` with file metadata
|
|
14
15
|
|
|
16
|
+
- **Watch changes in Package Directory**
|
|
17
|
+
Scans all changes in provided directory and updates `layout.json` accordingly
|
|
18
|
+
|
|
15
19
|
- **Manifest Integration**
|
|
16
20
|
Automatically updates `total_package_size` in `manifest.json`
|
|
17
21
|
- **Multiple Processing Modes**
|
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
40
40
|
const commander_1 = require("commander");
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
43
45
|
const processLayout_1 = require("./utils/processLayout");
|
|
44
46
|
// Define the program
|
|
45
47
|
const program = new commander_1.Command();
|
|
@@ -65,6 +67,10 @@ const logger = {
|
|
|
65
67
|
* msfs-layout "package1" "package2" "package3"
|
|
66
68
|
*
|
|
67
69
|
* @example
|
|
70
|
+
* // Watch directory for changes
|
|
71
|
+
* msfs-layout "./my-package" --watch
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
68
74
|
* // Show help
|
|
69
75
|
* msfs-layout --help
|
|
70
76
|
*/
|
|
@@ -77,6 +83,7 @@ program
|
|
|
77
83
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
78
84
|
.option('-d, --debug', 'Enable debug logging for troubleshooting')
|
|
79
85
|
.option('--no-manifest-check', 'Skip manifest.json existence check')
|
|
86
|
+
.option('-w, --watch', 'Watch directory for changes and regenerate automatically')
|
|
80
87
|
.action(async (directories, options) => {
|
|
81
88
|
await handleAction(directories, options);
|
|
82
89
|
})
|
|
@@ -97,15 +104,20 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
97
104
|
${chalk_1.default.dim('# Quiet mode - minimal output')}
|
|
98
105
|
msfs-layout ./my-package --quiet
|
|
99
106
|
|
|
107
|
+
${chalk_1.default.dim('# Watch directory for changes')}
|
|
108
|
+
msfs-layout ./my-package --watch
|
|
109
|
+
|
|
100
110
|
${chalk_1.default.bold('Notes:')}
|
|
101
111
|
• Each directory should contain a ${chalk_1.default.cyan('manifest.json')} file
|
|
102
112
|
• Creates/updates ${chalk_1.default.cyan('layout.json')} in the same directory
|
|
103
113
|
• Automatically excludes ${chalk_1.default.yellow('_CVT_')} directories
|
|
104
114
|
• Updates ${chalk_1.default.cyan('total_package_size')} in manifest.json
|
|
115
|
+
• Watch mode works with a ${chalk_1.default.yellow('single directory')} only
|
|
116
|
+
• Use ${chalk_1.default.cyan('Ctrl+C')} to exit watch mode
|
|
105
117
|
`);
|
|
106
118
|
// Handle the main action
|
|
107
119
|
async function handleAction(directories, options) {
|
|
108
|
-
const { force, quiet, debug, manifestCheck } = options;
|
|
120
|
+
const { force, quiet, debug, manifestCheck, watch } = options;
|
|
109
121
|
// Show header if not in quiet mode
|
|
110
122
|
if (!quiet) {
|
|
111
123
|
logger.header(`msfs-layout-generator`);
|
|
@@ -117,6 +129,16 @@ async function handleAction(directories, options) {
|
|
|
117
129
|
logger.info('Use msfs-layout --help for usage information.');
|
|
118
130
|
process.exit(1);
|
|
119
131
|
}
|
|
132
|
+
if (watch && directories.length > 1) {
|
|
133
|
+
logger.error('Watch mode only supports a single directory.');
|
|
134
|
+
logger.info('Please specify only one directory when using --watch flag.');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
// Handle watch mode
|
|
138
|
+
if (watch) {
|
|
139
|
+
await handleWatchMode(directories[0], { force, quiet, debug, manifestCheck });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
120
142
|
const errors = [];
|
|
121
143
|
const successes = [];
|
|
122
144
|
let totalFilesProcessed = 0;
|
|
@@ -131,8 +153,8 @@ async function handleAction(directories, options) {
|
|
|
131
153
|
logger.dim(` Debug: Resolved path: ${fullPath}`);
|
|
132
154
|
}
|
|
133
155
|
// Check if directory exists
|
|
134
|
-
if (!
|
|
135
|
-
|
|
156
|
+
if (!fs.existsSync(fullPath)) {
|
|
157
|
+
new Error(`Directory does not exist`);
|
|
136
158
|
}
|
|
137
159
|
// Run the main processing function
|
|
138
160
|
const result = await (0, processLayout_1.doProcessLayoutFileCli)(fullPath, {
|
|
@@ -191,7 +213,134 @@ async function handleAction(directories, options) {
|
|
|
191
213
|
process.exit(1);
|
|
192
214
|
}
|
|
193
215
|
}
|
|
194
|
-
|
|
216
|
+
async function handleWatchMode(dir, options) {
|
|
217
|
+
const { quiet, debug, manifestCheck, watchInterval, watchDebounce } = options;
|
|
218
|
+
const fullPath = path.resolve(dir);
|
|
219
|
+
if (!fs.existsSync(fullPath)) {
|
|
220
|
+
logger.error(`Directory does not exist: ${fullPath}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
if (!quiet) {
|
|
224
|
+
logger.info(`Watching: ${chalk_1.default.underline(fullPath)}`);
|
|
225
|
+
logger.info(`Press ${chalk_1.default.yellow('Ctrl+C')} to stop watching`);
|
|
226
|
+
console.log();
|
|
227
|
+
}
|
|
228
|
+
if (!quiet) {
|
|
229
|
+
logger.info(`Running initial processing...`);
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
await (0, processLayout_1.doProcessLayoutFileCli)(fullPath, {
|
|
233
|
+
force: true,
|
|
234
|
+
quiet,
|
|
235
|
+
debug,
|
|
236
|
+
checkManifest: manifestCheck
|
|
237
|
+
});
|
|
238
|
+
console.log();
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
logger.error(`Initial processing failed: ${error.message}`);
|
|
242
|
+
if (debug && error.stack) {
|
|
243
|
+
logger.dim(error.stack);
|
|
244
|
+
}
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
let debounceTimer;
|
|
248
|
+
let isProcessing = false;
|
|
249
|
+
let changeCount = 0;
|
|
250
|
+
const watcher = chokidar_1.default.watch(fullPath, {
|
|
251
|
+
ignored: [
|
|
252
|
+
path.join(fullPath, 'layout.json'),
|
|
253
|
+
path.join(fullPath, 'manifest.json')
|
|
254
|
+
],
|
|
255
|
+
ignoreInitial: true,
|
|
256
|
+
persistent: true,
|
|
257
|
+
interval: parseInt(watchInterval),
|
|
258
|
+
depth: 99
|
|
259
|
+
});
|
|
260
|
+
const processChanges = async () => {
|
|
261
|
+
if (isProcessing) {
|
|
262
|
+
if (debug) {
|
|
263
|
+
logger.dim('Skipping - already processing');
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
clearTimeout(debounceTimer);
|
|
268
|
+
debounceTimer = setTimeout(async () => {
|
|
269
|
+
isProcessing = true;
|
|
270
|
+
changeCount++;
|
|
271
|
+
try {
|
|
272
|
+
await (0, processLayout_1.doProcessLayoutFileCli)(fullPath, {
|
|
273
|
+
force: true,
|
|
274
|
+
quiet: true
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
279
|
+
if (!quiet) {
|
|
280
|
+
logger.error(`[${timestamp}] Failed to regenerate: ${error.message}`);
|
|
281
|
+
if (debug && error.stack) {
|
|
282
|
+
logger.dim(error.stack);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
isProcessing = false;
|
|
288
|
+
}
|
|
289
|
+
}, parseInt(watchDebounce));
|
|
290
|
+
};
|
|
291
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
292
|
+
watcher
|
|
293
|
+
.on('add', (filePath) => {
|
|
294
|
+
if (!quiet) {
|
|
295
|
+
logger.dim(`[${timestamp}] File added: ${path.relative(fullPath, filePath)}`);
|
|
296
|
+
}
|
|
297
|
+
processChanges();
|
|
298
|
+
})
|
|
299
|
+
.on('change', (filePath) => {
|
|
300
|
+
if (!quiet) {
|
|
301
|
+
logger.dim(`[${timestamp}] File changed: ${path.relative(fullPath, filePath)}`);
|
|
302
|
+
}
|
|
303
|
+
processChanges();
|
|
304
|
+
})
|
|
305
|
+
.on('unlink', (filePath) => {
|
|
306
|
+
if (!quiet) {
|
|
307
|
+
logger.dim(`[${timestamp}] File removed: ${path.relative(fullPath, filePath)}`);
|
|
308
|
+
}
|
|
309
|
+
processChanges();
|
|
310
|
+
})
|
|
311
|
+
.on('addDir', (dirPath) => {
|
|
312
|
+
if (!quiet) {
|
|
313
|
+
logger.dim(`[${timestamp}] Directory added: ${path.relative(fullPath, dirPath)}`);
|
|
314
|
+
}
|
|
315
|
+
processChanges();
|
|
316
|
+
})
|
|
317
|
+
.on('unlinkDir', (dirPath) => {
|
|
318
|
+
if (!quiet) {
|
|
319
|
+
logger.dim(`[${timestamp}] Directory removed: ${path.relative(fullPath, dirPath)}`);
|
|
320
|
+
}
|
|
321
|
+
processChanges();
|
|
322
|
+
})
|
|
323
|
+
.on('error', (error) => {
|
|
324
|
+
if (error instanceof Error) {
|
|
325
|
+
logger.error(`Watcher error: ${error.message}`);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
process.on('SIGINT', async () => {
|
|
329
|
+
if (!quiet) {
|
|
330
|
+
console.log();
|
|
331
|
+
logger.info('Stopping watch mode...');
|
|
332
|
+
}
|
|
333
|
+
await watcher.close();
|
|
334
|
+
if (!quiet) {
|
|
335
|
+
logger.success(`Watch mode stopped`);
|
|
336
|
+
logger.info(`Total changes processed: ${changeCount}`);
|
|
337
|
+
}
|
|
338
|
+
process.exit(0);
|
|
339
|
+
});
|
|
340
|
+
await new Promise(() => {
|
|
341
|
+
// This promise never resolves keeping the process alive
|
|
342
|
+
});
|
|
343
|
+
}
|
|
195
344
|
function formatFileSize(bytes) {
|
|
196
345
|
if (bytes === 0)
|
|
197
346
|
return '0 Bytes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msfs-layout-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Generate layout.json for MSFS community packages",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"chalk": "^5.6.2",
|
|
44
|
+
"chokidar": "^5.0.0",
|
|
44
45
|
"commander": "^14.0.2",
|
|
45
46
|
"glob": "^13.0.0"
|
|
46
47
|
}
|