@openfairygui/cli 0.1.0 → 0.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/dist/cli.mjs +3185 -2986
- package/package.json +1 -1
- package/src/cli.ts +175 -108
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NodeIO,
|
|
3
3
|
ProjectType,
|
|
4
|
+
} from '@openfairygui/core';
|
|
5
|
+
import {
|
|
6
|
+
inspect,
|
|
7
|
+
publish,
|
|
8
|
+
resolvePublishOptions,
|
|
9
|
+
restore,
|
|
10
|
+
type InspectReport,
|
|
11
|
+
type PublishOptions,
|
|
12
|
+
type RestoreFileSystem,
|
|
4
13
|
type RestoreImageCropInput,
|
|
5
14
|
type RestoreImageCropper,
|
|
6
15
|
type RestoreImageExtractInput,
|
|
7
16
|
type RestoreImageExtractor,
|
|
8
|
-
} from '@openfairygui/
|
|
9
|
-
import { inspect, publish, resolvePublishOptions, type InspectReport, type PublishOptions } from '@openfairygui/functions';
|
|
17
|
+
} from '@openfairygui/functions';
|
|
10
18
|
import fs from 'node:fs/promises';
|
|
11
19
|
import path from 'node:path';
|
|
12
20
|
import { parseArgs } from 'node:util';
|
|
13
|
-
|
|
21
|
+
|
|
14
22
|
const HELP = `
|
|
15
23
|
ofgui — FairyGUI Headless Authoring CLI
|
|
16
24
|
|
|
@@ -36,58 +44,58 @@ Restore options:
|
|
|
36
44
|
--project-type <name|id> Override restored project type; default is unity
|
|
37
45
|
|
|
38
46
|
Options:
|
|
39
|
-
--help, -h Show this help
|
|
40
|
-
--version, -v Show version
|
|
41
|
-
|
|
42
|
-
Input can be a .fairy file or a project root directory (auto-discovers .fairy file).
|
|
43
|
-
File extension and binary format are read from project settings.
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
/** Resolve input to a .fairy file path. Accepts a directory or a .fairy file. */
|
|
47
|
-
async function resolveFairyPath(input: string): Promise<string> {
|
|
48
|
-
const resolved = path.resolve(input);
|
|
49
|
-
const stat = await fs.stat(resolved);
|
|
50
|
-
|
|
51
|
-
if (stat.isFile() && resolved.endsWith('.fairy')) {
|
|
52
|
-
return resolved;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (stat.isDirectory()) {
|
|
56
|
-
// Scan for *.fairy in the directory
|
|
57
|
-
const entries = await fs.readdir(resolved);
|
|
58
|
-
const fairyFiles = entries.filter((e) => e.endsWith('.fairy'));
|
|
59
|
-
if (fairyFiles.length === 1) {
|
|
60
|
-
return path.join(resolved, fairyFiles[0]);
|
|
61
|
-
}
|
|
62
|
-
if (fairyFiles.length > 1) {
|
|
63
|
-
throw new Error(`Multiple .fairy files found in ${resolved}: ${fairyFiles.join(', ')}. Please specify one.`);
|
|
64
|
-
}
|
|
65
|
-
throw new Error(`No .fairy file found in ${resolved}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
throw new Error(`Input is not a .fairy file or directory: ${resolved}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
47
|
+
--help, -h Show this help
|
|
48
|
+
--version, -v Show version
|
|
49
|
+
|
|
50
|
+
Input can be a .fairy file or a project root directory (auto-discovers .fairy file).
|
|
51
|
+
File extension and binary format are read from project settings.
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
/** Resolve input to a .fairy file path. Accepts a directory or a .fairy file. */
|
|
55
|
+
async function resolveFairyPath(input: string): Promise<string> {
|
|
56
|
+
const resolved = path.resolve(input);
|
|
57
|
+
const stat = await fs.stat(resolved);
|
|
58
|
+
|
|
59
|
+
if (stat.isFile() && resolved.endsWith('.fairy')) {
|
|
60
|
+
return resolved;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (stat.isDirectory()) {
|
|
64
|
+
// Scan for *.fairy in the directory
|
|
65
|
+
const entries = await fs.readdir(resolved);
|
|
66
|
+
const fairyFiles = entries.filter((e) => e.endsWith('.fairy'));
|
|
67
|
+
if (fairyFiles.length === 1) {
|
|
68
|
+
return path.join(resolved, fairyFiles[0]);
|
|
69
|
+
}
|
|
70
|
+
if (fairyFiles.length > 1) {
|
|
71
|
+
throw new Error(`Multiple .fairy files found in ${resolved}: ${fairyFiles.join(', ')}. Please specify one.`);
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`No .fairy file found in ${resolved}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error(`Input is not a .fairy file or directory: ${resolved}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
async function main(): Promise<void> {
|
|
72
|
-
const args = process.argv.slice(2);
|
|
73
|
-
|
|
74
|
-
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
75
|
-
console.log(HELP);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
80
|
-
console.log('0.1.0');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const command = args[0];
|
|
85
|
-
const rest = args.slice(1);
|
|
86
|
-
|
|
87
|
-
switch (command) {
|
|
88
|
-
case 'inspect':
|
|
89
|
-
await cmdInspect(rest);
|
|
90
|
-
break;
|
|
80
|
+
const args = process.argv.slice(2);
|
|
81
|
+
|
|
82
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
83
|
+
console.log(HELP);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
88
|
+
console.log('0.1.0');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const command = args[0];
|
|
93
|
+
const rest = args.slice(1);
|
|
94
|
+
|
|
95
|
+
switch (command) {
|
|
96
|
+
case 'inspect':
|
|
97
|
+
await cmdInspect(rest);
|
|
98
|
+
break;
|
|
91
99
|
case 'publish':
|
|
92
100
|
await cmdPublish(rest);
|
|
93
101
|
break;
|
|
@@ -95,10 +103,10 @@ async function main(): Promise<void> {
|
|
|
95
103
|
await cmdRestore(rest);
|
|
96
104
|
break;
|
|
97
105
|
default:
|
|
98
|
-
console.error(`Unknown command: ${command}\n`);
|
|
99
|
-
console.log(HELP);
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
106
|
+
console.error(`Unknown command: ${command}\n`);
|
|
107
|
+
console.log(HELP);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
interface RestoreImageProcessors {
|
|
@@ -106,6 +114,63 @@ interface RestoreImageProcessors {
|
|
|
106
114
|
extractImage: RestoreImageExtractor;
|
|
107
115
|
}
|
|
108
116
|
|
|
117
|
+
function createNodeRestoreFs(): RestoreFileSystem {
|
|
118
|
+
return {
|
|
119
|
+
async readFile(filePath: string): Promise<string> {
|
|
120
|
+
return fs.readFile(filePath, 'utf-8');
|
|
121
|
+
},
|
|
122
|
+
async readFileRaw(filePath: string): Promise<Uint8Array> {
|
|
123
|
+
const buf = await fs.readFile(filePath);
|
|
124
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
125
|
+
},
|
|
126
|
+
async writeFile(filePath: string, content: string): Promise<void> {
|
|
127
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
128
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
129
|
+
},
|
|
130
|
+
async writeFileRaw(filePath: string, data: Uint8Array): Promise<void> {
|
|
131
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
132
|
+
await fs.writeFile(filePath, data);
|
|
133
|
+
},
|
|
134
|
+
async mkdir(dirPath: string): Promise<void> {
|
|
135
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
136
|
+
},
|
|
137
|
+
async readdir(dirPath: string): Promise<string[]> {
|
|
138
|
+
return fs.readdir(dirPath);
|
|
139
|
+
},
|
|
140
|
+
async exists(filePath: string): Promise<boolean> {
|
|
141
|
+
try {
|
|
142
|
+
await fs.access(filePath);
|
|
143
|
+
return true;
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
async isFile(filePath: string): Promise<boolean> {
|
|
149
|
+
try {
|
|
150
|
+
return (await fs.stat(filePath)).isFile();
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
async resolvePath(filePath: string): Promise<string> {
|
|
156
|
+
try {
|
|
157
|
+
return await fs.realpath(filePath);
|
|
158
|
+
} catch {
|
|
159
|
+
return path.resolve(filePath);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async rm(targetPath: string, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
|
|
163
|
+
await fs.rm(targetPath, { recursive: options?.recursive ?? false, force: options?.force ?? false });
|
|
164
|
+
},
|
|
165
|
+
join(...paths: string[]): string {
|
|
166
|
+
return path.join(...paths);
|
|
167
|
+
},
|
|
168
|
+
dirname(filePath: string): string {
|
|
169
|
+
return path.dirname(filePath);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
109
174
|
async function createRestoreImageProcessors(): Promise<RestoreImageProcessors> {
|
|
110
175
|
let sharp: any;
|
|
111
176
|
try {
|
|
@@ -140,7 +205,7 @@ async function createRestoreImageProcessors(): Promise<RestoreImageProcessors> {
|
|
|
140
205
|
|| input.offsetY + info.height > input.expectedHeight
|
|
141
206
|
) {
|
|
142
207
|
throw new Error(
|
|
143
|
-
`restore: Cropped image does not fit original canvas for ${
|
|
208
|
+
`restore: Cropped image does not fit original canvas for ${targetPath}: `
|
|
144
209
|
+ `crop ${info.width}x${info.height} at ${input.offsetX},${input.offsetY}, `
|
|
145
210
|
+ `canvas ${input.expectedWidth}x${input.expectedHeight}`,
|
|
146
211
|
);
|
|
@@ -190,41 +255,41 @@ async function createRestoreImageProcessors(): Promise<RestoreImageProcessors> {
|
|
|
190
255
|
},
|
|
191
256
|
};
|
|
192
257
|
}
|
|
193
|
-
|
|
258
|
+
|
|
194
259
|
async function cmdInspect(args: string[]): Promise<void> {
|
|
195
260
|
if (args.length === 0) {
|
|
196
261
|
console.error('Usage: ofgui inspect <project-dir>');
|
|
197
262
|
process.exit(1);
|
|
198
263
|
}
|
|
199
|
-
|
|
200
|
-
const fairyPath = await resolveFairyPath(args[0]);
|
|
201
|
-
console.log(`Project: ${fairyPath}\n`);
|
|
202
|
-
|
|
203
|
-
const io = new NodeIO();
|
|
204
|
-
const doc = await io.readProject(fairyPath);
|
|
205
|
-
const report = inspect(doc);
|
|
206
|
-
|
|
207
|
-
printReport(report);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function printReport(report: InspectReport): void {
|
|
211
|
-
console.log(`ID: ${report.projectId}`);
|
|
212
|
-
console.log(`Type: ${report.projectType}, Version: ${report.version}`);
|
|
213
|
-
console.log(`\nPackages: ${report.totals.packages}`);
|
|
214
|
-
console.log(` Images: ${report.totals.images}`);
|
|
215
|
-
console.log(` Sounds: ${report.totals.sounds}`);
|
|
216
|
-
console.log(` Fonts: ${report.totals.fonts}`);
|
|
217
|
-
console.log(` MovieClips: ${report.totals.movieClips}`);
|
|
218
|
-
console.log(` Components: ${report.totals.components}`);
|
|
219
|
-
console.log(` DisplayObjs: ${report.totals.displayObjects}`);
|
|
220
|
-
console.log(` Gears: ${report.totals.gears}`);
|
|
221
|
-
console.log(` Controllers: ${report.totals.controllers}`);
|
|
222
|
-
console.log(` Transitions: ${report.totals.transitions}`);
|
|
223
|
-
|
|
224
|
-
console.log('\nPackage details:');
|
|
225
|
-
for (const pkg of report.packages) {
|
|
226
|
-
const res = pkg.resources;
|
|
227
|
-
console.log(` ${pkg.name} (${pkg.id}): ${res.images.count} img, ${res.sounds.count} snd, ${res.fonts.count} font, ${res.components.count} comp`);
|
|
264
|
+
|
|
265
|
+
const fairyPath = await resolveFairyPath(args[0]);
|
|
266
|
+
console.log(`Project: ${fairyPath}\n`);
|
|
267
|
+
|
|
268
|
+
const io = new NodeIO();
|
|
269
|
+
const doc = await io.readProject(fairyPath);
|
|
270
|
+
const report = inspect(doc);
|
|
271
|
+
|
|
272
|
+
printReport(report);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function printReport(report: InspectReport): void {
|
|
276
|
+
console.log(`ID: ${report.projectId}`);
|
|
277
|
+
console.log(`Type: ${report.projectType}, Version: ${report.version}`);
|
|
278
|
+
console.log(`\nPackages: ${report.totals.packages}`);
|
|
279
|
+
console.log(` Images: ${report.totals.images}`);
|
|
280
|
+
console.log(` Sounds: ${report.totals.sounds}`);
|
|
281
|
+
console.log(` Fonts: ${report.totals.fonts}`);
|
|
282
|
+
console.log(` MovieClips: ${report.totals.movieClips}`);
|
|
283
|
+
console.log(` Components: ${report.totals.components}`);
|
|
284
|
+
console.log(` DisplayObjs: ${report.totals.displayObjects}`);
|
|
285
|
+
console.log(` Gears: ${report.totals.gears}`);
|
|
286
|
+
console.log(` Controllers: ${report.totals.controllers}`);
|
|
287
|
+
console.log(` Transitions: ${report.totals.transitions}`);
|
|
288
|
+
|
|
289
|
+
console.log('\nPackage details:');
|
|
290
|
+
for (const pkg of report.packages) {
|
|
291
|
+
const res = pkg.resources;
|
|
292
|
+
console.log(` ${pkg.name} (${pkg.id}): ${res.images.count} img, ${res.sounds.count} snd, ${res.fonts.count} font, ${res.components.count} comp`);
|
|
228
293
|
}
|
|
229
294
|
}
|
|
230
295
|
|
|
@@ -282,8 +347,10 @@ async function cmdRestore(args: string[]): Promise<void> {
|
|
|
282
347
|
const { cropImage, extractImage } = await createRestoreImageProcessors();
|
|
283
348
|
|
|
284
349
|
console.log(`Restoring published FairyGUI project: ${releaseDir}`);
|
|
285
|
-
const
|
|
286
|
-
|
|
350
|
+
const result = await restore({
|
|
351
|
+
inputDir: releaseDir,
|
|
352
|
+
output: outputDir,
|
|
353
|
+
fs: createNodeRestoreFs(),
|
|
287
354
|
packages: pkgFilter,
|
|
288
355
|
force: values.force,
|
|
289
356
|
projectType,
|
|
@@ -311,16 +378,16 @@ async function cmdPublish(args: string[]): Promise<void> {
|
|
|
311
378
|
},
|
|
312
379
|
allowPositionals: true,
|
|
313
380
|
});
|
|
314
|
-
|
|
381
|
+
|
|
315
382
|
if (positionals.length === 0 || !values.output) {
|
|
316
383
|
console.error('Usage: ofgui publish <project-dir> --output <dir> [--compressed] [--packages a,b,c] [--branch name]');
|
|
317
384
|
process.exit(1);
|
|
318
385
|
}
|
|
319
|
-
|
|
320
|
-
const fairyPath = await resolveFairyPath(positionals[0]);
|
|
321
|
-
const projectDir = path.dirname(fairyPath);
|
|
322
|
-
const outputDir = path.resolve(values.output);
|
|
323
|
-
|
|
386
|
+
|
|
387
|
+
const fairyPath = await resolveFairyPath(positionals[0]);
|
|
388
|
+
const projectDir = path.dirname(fairyPath);
|
|
389
|
+
const outputDir = path.resolve(values.output);
|
|
390
|
+
|
|
324
391
|
console.log(`Reading project: ${fairyPath}`);
|
|
325
392
|
const io = new NodeIO();
|
|
326
393
|
const doc = await io.readProject(fairyPath);
|
|
@@ -353,7 +420,7 @@ async function cmdPublish(args: string[]): Promise<void> {
|
|
|
353
420
|
try {
|
|
354
421
|
const sharp = await import('sharp');
|
|
355
422
|
encoder = sharp.default ?? sharp;
|
|
356
|
-
console.log('Sharp loaded — atlas PNGs will be generated.');
|
|
423
|
+
console.log('Sharp loaded — atlas PNGs will be generated.');
|
|
357
424
|
} catch {
|
|
358
425
|
console.log('Sharp not available — atlas PNGs will NOT be generated (layout only).');
|
|
359
426
|
console.log(' Install sharp to enable: pnpm add sharp');
|
|
@@ -367,7 +434,7 @@ async function cmdPublish(args: string[]): Promise<void> {
|
|
|
367
434
|
async writeFileRaw(filePath: string, data: Uint8Array): Promise<void> {
|
|
368
435
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
369
436
|
await fs.writeFile(filePath, data);
|
|
370
|
-
},
|
|
437
|
+
},
|
|
371
438
|
async mkdir(dirPath: string): Promise<void> {
|
|
372
439
|
await fs.mkdir(dirPath, { recursive: true });
|
|
373
440
|
},
|
|
@@ -381,7 +448,7 @@ async function cmdPublish(args: string[]): Promise<void> {
|
|
|
381
448
|
return path.join(...paths);
|
|
382
449
|
},
|
|
383
450
|
};
|
|
384
|
-
|
|
451
|
+
|
|
385
452
|
await doc.transform(publish({
|
|
386
453
|
output: outputDir,
|
|
387
454
|
compressed: resolved.compressed,
|
|
@@ -393,11 +460,11 @@ async function cmdPublish(args: string[]): Promise<void> {
|
|
|
393
460
|
atlas: atlasConfig,
|
|
394
461
|
branch: values.branch,
|
|
395
462
|
}));
|
|
396
|
-
|
|
397
|
-
console.log(`\nDone! Output: ${outputDir}`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
main().catch((err) => {
|
|
401
|
-
console.error(err);
|
|
402
|
-
process.exit(1);
|
|
403
|
-
});
|
|
463
|
+
|
|
464
|
+
console.log(`\nDone! Output: ${outputDir}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
main().catch((err) => {
|
|
468
|
+
console.error(err);
|
|
469
|
+
process.exit(1);
|
|
470
|
+
});
|