@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.
Files changed (3) hide show
  1. package/dist/cli.mjs +3185 -2986
  2. package/package.json +1 -1
  3. package/src/cli.ts +175 -108
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfairygui/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "FairyGUI Headless Authoring SDK — command-line interface.",
5
5
  "author": "OpenFairyGUI Contributors",
6
6
  "license": "MIT",
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/core';
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 ${input.outputPath}: `
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 io = new NodeIO();
286
- const result = await io.restorePublishedProject(releaseDir, outputDir, {
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
+ });