msfs-layout-generator 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +674 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +218 -0
  4. package/dist/constants/constants.d.ts +1 -0
  5. package/dist/constants/constants.js +8 -0
  6. package/dist/errors/ManifestWritingError.d.ts +3 -0
  7. package/dist/errors/ManifestWritingError.js +10 -0
  8. package/dist/errors/ReadingDirError.d.ts +3 -0
  9. package/dist/errors/ReadingDirError.js +10 -0
  10. package/dist/errors/ReadingJsonError.d.ts +3 -0
  11. package/dist/errors/ReadingJsonError.js +10 -0
  12. package/dist/errors/index.d.ts +4 -0
  13. package/dist/errors/index.js +9 -0
  14. package/dist/index.d.ts +26 -0
  15. package/dist/index.js +30 -0
  16. package/dist/types/cli.d.ts +2 -0
  17. package/dist/types/constants/constants.d.ts +1 -0
  18. package/dist/types/errors/ManifestWritingError.d.ts +3 -0
  19. package/dist/types/errors/ReadingDirError.d.ts +3 -0
  20. package/dist/types/errors/ReadingJsonError.d.ts +3 -0
  21. package/dist/types/errors/index.d.ts +4 -0
  22. package/dist/types/index.d.ts +26 -0
  23. package/dist/types/utils/doExcludeFile.d.ts +1 -0
  24. package/dist/types/utils/doProcessLayoutFile.d.ts +1 -0
  25. package/dist/types/utils/doProcessLayoutFileCli.d.ts +17 -0
  26. package/dist/types/utils/doUpdateManifest.d.ts +1 -0
  27. package/dist/types/utils/getAllFiles.d.ts +1 -0
  28. package/dist/types/utils/getWindowsFileTime.d.ts +1 -0
  29. package/dist/types/utils/readJson.d.ts +1 -0
  30. package/dist/utils/doExcludeFile.d.ts +1 -0
  31. package/dist/utils/doExcludeFile.js +46 -0
  32. package/dist/utils/doProcessLayoutFile.d.ts +1 -0
  33. package/dist/utils/doProcessLayoutFile.js +109 -0
  34. package/dist/utils/doProcessLayoutFileCli.js +220 -0
  35. package/dist/utils/doUpdateManifest.d.ts +1 -0
  36. package/dist/utils/doUpdateManifest.js +23 -0
  37. package/dist/utils/getAllFiles.d.ts +1 -0
  38. package/dist/utils/getAllFiles.js +65 -0
  39. package/dist/utils/getWindowsFileTime.d.ts +1 -0
  40. package/dist/utils/getWindowsFileTime.js +11 -0
  41. package/dist/utils/readJson.d.ts +1 -0
  42. package/dist/utils/readJson.js +17 -0
  43. package/package.json +43 -0
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.doProcessLayoutFileCli = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const getAllFiles_1 = require("./getAllFiles");
39
+ const doExcludeFile_1 = require("./doExcludeFile");
40
+ const promises_1 = require("fs/promises");
41
+ const getWindowsFileTime_1 = require("./getWindowsFileTime");
42
+ const doUpdateManifest_1 = require("./doUpdateManifest");
43
+ const doProcessLayoutFileCli = async (packageDir, options = {}) => {
44
+ const { force = false, quiet = false, debug = false, checkManifest = true, skipManifestUpdate = false } = options;
45
+ const layoutPathFile = path.join(packageDir, 'layout.json');
46
+ const manifestPath = path.join(packageDir, 'manifest.json');
47
+ // Validate input directory exists
48
+ try {
49
+ await (0, promises_1.access)(packageDir, promises_1.constants.F_OK);
50
+ }
51
+ catch {
52
+ return {
53
+ fileCount: 0,
54
+ totalSize: 0,
55
+ layoutPath: layoutPathFile,
56
+ manifestPath,
57
+ skippedFiles: 0,
58
+ success: false,
59
+ message: `Directory does not exist: ${packageDir}`
60
+ };
61
+ }
62
+ // Check for manifest.json if required
63
+ if (checkManifest) {
64
+ try {
65
+ await (0, promises_1.access)(manifestPath, promises_1.constants.F_OK);
66
+ }
67
+ catch {
68
+ return {
69
+ fileCount: 0,
70
+ totalSize: 0,
71
+ layoutPath: layoutPathFile,
72
+ manifestPath,
73
+ skippedFiles: 0,
74
+ success: false,
75
+ message: `manifest.json not found in ${packageDir}`
76
+ };
77
+ }
78
+ }
79
+ // Check if layout.json already exists
80
+ try {
81
+ await (0, promises_1.access)(layoutPathFile, promises_1.constants.F_OK);
82
+ if (!force) {
83
+ return {
84
+ fileCount: 0,
85
+ totalSize: 0,
86
+ layoutPath: layoutPathFile,
87
+ manifestPath,
88
+ skippedFiles: 0,
89
+ success: false,
90
+ message: `layout.json already exists. Use --force to overwrite.`
91
+ };
92
+ }
93
+ if (debug && !quiet) {
94
+ console.log(`Debug: layout.json exists, will overwrite due to --force flag`);
95
+ }
96
+ }
97
+ catch {
98
+ // layout.json doesn't exist, that's fine
99
+ if (debug && !quiet) {
100
+ console.log(`Debug: No existing layout.json found, creating new one`);
101
+ }
102
+ }
103
+ let totalPackageSize = 0;
104
+ const layout = { content: [] };
105
+ let hasLongPath = false;
106
+ let excludedCount = 0;
107
+ try {
108
+ const allFiles = await (0, getAllFiles_1.getAllFiles)(packageDir);
109
+ if (debug && !quiet) {
110
+ console.log(`Debug: Found ${allFiles.length} total files in ${packageDir}`);
111
+ }
112
+ if (allFiles.length === 0) {
113
+ return {
114
+ fileCount: 0,
115
+ totalSize: 0,
116
+ layoutPath: layoutPathFile,
117
+ manifestPath,
118
+ skippedFiles: 0,
119
+ success: false,
120
+ message: 'No files found in package directory'
121
+ };
122
+ }
123
+ for (const file of allFiles) {
124
+ // Check for long file paths (Windows limitation)
125
+ if (file.length > 259) {
126
+ hasLongPath = true;
127
+ if (debug && !quiet) {
128
+ console.log(`Debug: Skipping long path: ${file}`);
129
+ }
130
+ continue;
131
+ }
132
+ const relativePath = path.relative(packageDir, file).split(path.sep).join('/');
133
+ const isExcluded = (0, doExcludeFile_1.doExcludeFile)(relativePath);
134
+ try {
135
+ const stats = await (0, promises_1.stat)(file);
136
+ totalPackageSize += stats.size;
137
+ if (isExcluded) {
138
+ excludedCount++;
139
+ if (debug && !quiet) {
140
+ console.log(`Debug: Excluding file: ${relativePath}`);
141
+ }
142
+ continue;
143
+ }
144
+ const content = {
145
+ path: relativePath,
146
+ size: stats.size,
147
+ date: (0, getWindowsFileTime_1.getWindowsFileTime)(stats.mtime)
148
+ };
149
+ layout.content.push(content);
150
+ }
151
+ catch (error) {
152
+ if (debug && !quiet) {
153
+ console.log(`Debug: Error processing file ${file}: ${error.message}`);
154
+ }
155
+ excludedCount++;
156
+ }
157
+ }
158
+ if (hasLongPath && !quiet) {
159
+ console.log(`Warning: One or more file paths exceed 259 characters and were skipped.`);
160
+ }
161
+ if (layout.content.length === 0) {
162
+ return {
163
+ fileCount: 0,
164
+ totalSize: 0,
165
+ layoutPath: layoutPathFile,
166
+ manifestPath,
167
+ skippedFiles: excludedCount,
168
+ success: false,
169
+ message: 'No valid files to include in layout.json'
170
+ };
171
+ }
172
+ // Sort content by path for consistent output
173
+ layout.content.sort((a, b) => a.path.localeCompare(b.path));
174
+ // Write layout.json with LF line endings
175
+ const json = JSON.stringify(layout, null, 2).replace(/\r\n/g, '\n');
176
+ await (0, promises_1.writeFile)(layoutPathFile, json, 'utf8');
177
+ // Add layout.json size to total
178
+ const layoutStats = await (0, promises_1.stat)(layoutPathFile);
179
+ totalPackageSize += layoutStats.size;
180
+ // Update manifest.json if it exists and not skipped
181
+ if (!skipManifestUpdate) {
182
+ try {
183
+ await (0, promises_1.access)(manifestPath, promises_1.constants.F_OK);
184
+ await (0, doUpdateManifest_1.doUpdateManifest)(manifestPath, totalPackageSize);
185
+ if (debug && !quiet) {
186
+ console.log(`Debug: Updated manifest.json with total_package_size`);
187
+ }
188
+ }
189
+ catch {
190
+ // manifest doesn't exist, skip
191
+ if (debug && !quiet) {
192
+ console.log(`Debug: manifest.json not found, skipping update`);
193
+ }
194
+ }
195
+ }
196
+ if (!quiet) {
197
+ console.log(`Successfully updated layout.json with ${layout.content.length} files`);
198
+ }
199
+ return {
200
+ fileCount: layout.content.length,
201
+ totalSize: totalPackageSize,
202
+ layoutPath: layoutPathFile,
203
+ manifestPath,
204
+ skippedFiles: excludedCount,
205
+ success: true
206
+ };
207
+ }
208
+ catch (error) {
209
+ return {
210
+ fileCount: 0,
211
+ totalSize: 0,
212
+ layoutPath: layoutPathFile,
213
+ manifestPath,
214
+ skippedFiles: excludedCount,
215
+ success: false,
216
+ message: `Error processing ${packageDir}: ${error.message}`
217
+ };
218
+ }
219
+ };
220
+ exports.doProcessLayoutFileCli = doProcessLayoutFileCli;
@@ -0,0 +1 @@
1
+ export declare const doUpdateManifest: (manifestPath: string, totalPackageSize: number) => Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doUpdateManifest = void 0;
4
+ const promises_1 = require("fs/promises");
5
+ const _errors_1 = require("../errors/index.js");
6
+ const doUpdateManifest = async (manifestPath, totalPackageSize) => {
7
+ try {
8
+ const manifestContent = await (0, promises_1.readFile)(manifestPath, 'utf8');
9
+ const manifest = JSON.parse(manifestContent);
10
+ if (manifest.total_package_size !== undefined) {
11
+ manifest.total_package_size = totalPackageSize.toString().padStart(20, '0');
12
+ const json = JSON.stringify(manifest, null, 2);
13
+ await (0, promises_1.writeFile)(manifestPath, json, 'utf8');
14
+ }
15
+ }
16
+ catch (e) {
17
+ if (e instanceof Error) {
18
+ throw new _errors_1.ManifestWritingError(`Failed to read JSON at ${manifestPath}, size ${totalPackageSize}: ${e.message}.`);
19
+ }
20
+ throw new _errors_1.ManifestWritingError("Failed to read JSON due to unexpected error.");
21
+ }
22
+ };
23
+ exports.doUpdateManifest = doUpdateManifest;
@@ -0,0 +1 @@
1
+ export declare const getAllFiles: (dirPath: string) => Promise<string[]>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getAllFiles = void 0;
37
+ const promises_1 = require("fs/promises");
38
+ const path = __importStar(require("node:path"));
39
+ const _errors_1 = require("../errors/index.js");
40
+ const getAllFiles = async (dirPath) => {
41
+ const files = [];
42
+ async function readDirectory(currentPath) {
43
+ try {
44
+ for (const item of await (0, promises_1.readdir)(currentPath)) {
45
+ const itemPath = path.join(currentPath, item);
46
+ const receivedFile = await (0, promises_1.stat)(itemPath);
47
+ if (receivedFile.isDirectory()) {
48
+ await readDirectory(itemPath);
49
+ }
50
+ else {
51
+ files.push(itemPath);
52
+ }
53
+ }
54
+ }
55
+ catch (e) {
56
+ if (e instanceof Error) {
57
+ throw new _errors_1.ReadingDirError(`Failed to read directory ${path}: ${e.message}.`);
58
+ }
59
+ throw new _errors_1.ReadingDirError(`Failed to read directory. Path: ${path}`);
60
+ }
61
+ }
62
+ await readDirectory(dirPath);
63
+ return files;
64
+ };
65
+ exports.getAllFiles = getAllFiles;
@@ -0,0 +1 @@
1
+ export declare const getWindowsFileTime: (date: Date) => number;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWindowsFileTime = void 0;
4
+ const getWindowsFileTime = (date) => {
5
+ const windowsEpoch = new Date('1601-01-01T00:00:00Z').getTime();
6
+ const unixEpoch = new Date('1970-01-01T00:00:00Z').getTime();
7
+ const millisecondsToWindowsEpoch = windowsEpoch - unixEpoch;
8
+ const fileTime = (date.getTime() - millisecondsToWindowsEpoch) * 10000;
9
+ return Math.floor(fileTime);
10
+ };
11
+ exports.getWindowsFileTime = getWindowsFileTime;
@@ -0,0 +1 @@
1
+ export declare function readJson<T = unknown>(path: string): Promise<T | null>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readJson = readJson;
4
+ const promises_1 = require("fs/promises");
5
+ const _errors_1 = require("../errors/index.js");
6
+ async function readJson(path) {
7
+ try {
8
+ const data = await (0, promises_1.readFile)(path, "utf8");
9
+ return JSON.parse(data);
10
+ }
11
+ catch (e) {
12
+ if (e instanceof Error) {
13
+ throw new _errors_1.ReadingJsonError(`Failed to read JSON at ${path}: ${e.message}.`);
14
+ }
15
+ throw new _errors_1.ReadingJsonError("Failed to read JSON due to unexpected error.");
16
+ }
17
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "msfs-layout-generator",
3
+ "version": "0.1.0",
4
+ "description": "Generate layout.json for MSFS community packages",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
12
+ }
13
+ },
14
+ "sideEffects": false,
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc && tsc-alias",
20
+ "dev": "ts-node src/cli.ts",
21
+ "start": "node dist/cli.js",
22
+ "cli:test": "bun src/cli.ts --help"
23
+ },
24
+ "bin": {
25
+ "msfs-layout": "dist/cli.js",
26
+ "msfs-layout-generator": "dist/cli.js"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bun": "latest",
30
+ "tsc-alias": "^1.8.16"
31
+ },
32
+ "peerDependencies": {
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "dependencies": {
36
+ "@types/node": "^25.0.3",
37
+ "chalk": "^5.6.2",
38
+ "commander": "^14.0.2",
39
+ "glob": "^13.0.0",
40
+ "ts-node": "^10.9.2",
41
+ "tsconfig-paths": "^4.2.0"
42
+ }
43
+ }