unity-hub-cli 0.20.0 → 0.22.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/dist/index.js +53 -130
- package/package.json +13 -12
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { execSync, spawnSync } from "child_process";
|
|
5
5
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
|
-
import { dirname as dirname2, join as
|
|
7
|
+
import { dirname as dirname2, join as join7 } from "path";
|
|
8
8
|
import process3 from "process";
|
|
9
9
|
import { createInterface as createInterface2 } from "readline";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
@@ -12,6 +12,10 @@ import chalk from "chalk";
|
|
|
12
12
|
import { render } from "ink";
|
|
13
13
|
|
|
14
14
|
// src/application/usecases.ts
|
|
15
|
+
import {
|
|
16
|
+
launch as launchUnity,
|
|
17
|
+
updateLastModifiedIfExists
|
|
18
|
+
} from "launch-unity";
|
|
15
19
|
var ListProjectsUseCase = class {
|
|
16
20
|
constructor(unityHubProjectsReader, gitRepositoryInfoReader, unityProjectOptionsReader, lockReader, unityProcessReader) {
|
|
17
21
|
this.unityHubProjectsReader = unityHubProjectsReader;
|
|
@@ -56,12 +60,18 @@ var LaunchCancelledError = class extends Error {
|
|
|
56
60
|
this.name = "LaunchCancelledError";
|
|
57
61
|
}
|
|
58
62
|
};
|
|
63
|
+
async function launchUnitySilently(opts) {
|
|
64
|
+
const originalLog = console.log;
|
|
65
|
+
console.log = () => {
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
await launchUnity(opts);
|
|
69
|
+
} finally {
|
|
70
|
+
console.log = originalLog;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
59
73
|
var LaunchProjectUseCase = class {
|
|
60
|
-
constructor(
|
|
61
|
-
this.editorPathResolver = editorPathResolver;
|
|
62
|
-
this.processLauncher = processLauncher;
|
|
63
|
-
this.unityHubProjectsReader = unityHubProjectsReader;
|
|
64
|
-
this.unityProjectOptionsReader = unityProjectOptionsReader;
|
|
74
|
+
constructor(unityProcessLockChecker) {
|
|
65
75
|
this.unityProcessLockChecker = unityProcessLockChecker;
|
|
66
76
|
}
|
|
67
77
|
async execute(project) {
|
|
@@ -69,13 +79,12 @@ var LaunchProjectUseCase = class {
|
|
|
69
79
|
if (lockDecision === "skip") {
|
|
70
80
|
throw new LaunchCancelledError();
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
detached: true
|
|
82
|
+
await launchUnitySilently({
|
|
83
|
+
projectPath: project.path,
|
|
84
|
+
unityArgs: [],
|
|
85
|
+
unityVersion: project.version.value
|
|
77
86
|
});
|
|
78
|
-
await
|
|
87
|
+
await updateLastModifiedIfExists(project.path, /* @__PURE__ */ new Date());
|
|
79
88
|
}
|
|
80
89
|
};
|
|
81
90
|
var TerminateProjectUseCase = class {
|
|
@@ -215,63 +224,11 @@ var LaunchEditorOnlyUseCase = class {
|
|
|
215
224
|
}
|
|
216
225
|
};
|
|
217
226
|
|
|
218
|
-
// src/infrastructure/editor.ts
|
|
219
|
-
import { constants } from "fs";
|
|
220
|
-
import { access } from "fs/promises";
|
|
221
|
-
import { join } from "path";
|
|
222
|
-
var UNITY_EDITOR_BASE = "/Applications/Unity/Hub/Editor";
|
|
223
|
-
var UNITY_BINARY_PATH = "Unity.app/Contents/MacOS/Unity";
|
|
224
|
-
var MacEditorPathResolver = class {
|
|
225
|
-
async resolve(version) {
|
|
226
|
-
const editorPath = join(UNITY_EDITOR_BASE, version.value, UNITY_BINARY_PATH);
|
|
227
|
-
try {
|
|
228
|
-
await access(editorPath, constants.X_OK);
|
|
229
|
-
} catch {
|
|
230
|
-
throw new Error(`Unity Editor not found for version ${version.value}.`);
|
|
231
|
-
}
|
|
232
|
-
return editorPath;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// src/infrastructure/editor.win.ts
|
|
237
|
-
import { constants as constants2 } from "fs";
|
|
238
|
-
import { access as access2 } from "fs/promises";
|
|
239
|
-
import { join as join2 } from "path";
|
|
240
|
-
var buildCandidateBases = () => {
|
|
241
|
-
const candidates = [];
|
|
242
|
-
const programFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
|
|
243
|
-
const programW6432 = process.env.ProgramW6432 ?? process.env.PROGRAMFILES;
|
|
244
|
-
const localAppData = process.env.LOCALAPPDATA;
|
|
245
|
-
candidates.push(join2(programFiles, "Unity", "Hub", "Editor"));
|
|
246
|
-
if (programW6432) {
|
|
247
|
-
candidates.push(join2(programW6432, "Unity", "Hub", "Editor"));
|
|
248
|
-
}
|
|
249
|
-
if (localAppData) {
|
|
250
|
-
candidates.push(join2(localAppData, "Unity", "Hub", "Editor"));
|
|
251
|
-
}
|
|
252
|
-
return Array.from(new Set(candidates));
|
|
253
|
-
};
|
|
254
|
-
var WinEditorPathResolver = class {
|
|
255
|
-
async resolve(version) {
|
|
256
|
-
const tried = [];
|
|
257
|
-
for (const base of buildCandidateBases()) {
|
|
258
|
-
const candidate = join2(base, version.value, "Editor", "Unity.exe");
|
|
259
|
-
try {
|
|
260
|
-
await access2(candidate, constants2.F_OK);
|
|
261
|
-
return candidate;
|
|
262
|
-
} catch {
|
|
263
|
-
tried.push(candidate);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
throw new Error(`Unity Editor not found for version ${version.value}. Tried: ${tried.join(" , ")}`);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
227
|
// src/infrastructure/externalEditor.ts
|
|
271
228
|
import { execFile } from "child_process";
|
|
272
|
-
import { constants
|
|
273
|
-
import { access
|
|
274
|
-
import { basename, join
|
|
229
|
+
import { constants, existsSync } from "fs";
|
|
230
|
+
import { access } from "fs/promises";
|
|
231
|
+
import { basename, join } from "path";
|
|
275
232
|
import { promisify } from "util";
|
|
276
233
|
var execFileAsync = promisify(execFile);
|
|
277
234
|
var PLIST_DOMAIN = "com.unity3d.UnityEditor5.x";
|
|
@@ -293,7 +250,7 @@ var MacExternalEditorPathReader = class {
|
|
|
293
250
|
return { status: "not_configured" };
|
|
294
251
|
}
|
|
295
252
|
try {
|
|
296
|
-
await
|
|
253
|
+
await access(configuredPath, constants.F_OK);
|
|
297
254
|
} catch {
|
|
298
255
|
return { status: "not_found", configuredPath };
|
|
299
256
|
}
|
|
@@ -318,7 +275,7 @@ var MacExternalEditorLauncher = class {
|
|
|
318
275
|
let targetPath = projectRoot;
|
|
319
276
|
if (prefersSlnFile(editorPath)) {
|
|
320
277
|
const projectName = basename(projectRoot);
|
|
321
|
-
const slnFilePath =
|
|
278
|
+
const slnFilePath = join(projectRoot, `${projectName}.sln`);
|
|
322
279
|
if (existsSync(slnFilePath)) {
|
|
323
280
|
targetPath = slnFilePath;
|
|
324
281
|
}
|
|
@@ -329,9 +286,9 @@ var MacExternalEditorLauncher = class {
|
|
|
329
286
|
|
|
330
287
|
// src/infrastructure/externalEditor.win.ts
|
|
331
288
|
import { execFile as execFile2, spawn } from "child_process";
|
|
332
|
-
import { constants as
|
|
333
|
-
import { access as
|
|
334
|
-
import { basename as basename2, join as
|
|
289
|
+
import { constants as constants2, existsSync as existsSync2 } from "fs";
|
|
290
|
+
import { access as access2 } from "fs/promises";
|
|
291
|
+
import { basename as basename2, join as join2 } from "path";
|
|
335
292
|
import { promisify as promisify2 } from "util";
|
|
336
293
|
|
|
337
294
|
// src/presentation/utils/path.ts
|
|
@@ -429,7 +386,7 @@ var WinExternalEditorPathReader = class {
|
|
|
429
386
|
return { status: "not_configured" };
|
|
430
387
|
}
|
|
431
388
|
try {
|
|
432
|
-
await
|
|
389
|
+
await access2(configuredPath, constants2.F_OK);
|
|
433
390
|
} catch {
|
|
434
391
|
return { status: "not_found", configuredPath };
|
|
435
392
|
}
|
|
@@ -454,7 +411,7 @@ var WinExternalEditorLauncher = class {
|
|
|
454
411
|
let targetPath = projectRoot;
|
|
455
412
|
if (prefersSlnFile2(editorPath)) {
|
|
456
413
|
const projectName = basename2(projectRoot);
|
|
457
|
-
const slnFilePath =
|
|
414
|
+
const slnFilePath = join2(projectRoot, `${projectName}.sln`);
|
|
458
415
|
if (existsSync2(slnFilePath)) {
|
|
459
416
|
targetPath = slnFilePath;
|
|
460
417
|
}
|
|
@@ -482,7 +439,7 @@ var WinExternalEditorLauncher = class {
|
|
|
482
439
|
|
|
483
440
|
// src/infrastructure/git.ts
|
|
484
441
|
import { readFile, stat } from "fs/promises";
|
|
485
|
-
import { dirname, join as
|
|
442
|
+
import { dirname, join as join3, resolve } from "path";
|
|
486
443
|
var HEAD_FILE = "HEAD";
|
|
487
444
|
var GIT_DIR = ".git";
|
|
488
445
|
var MAX_ASCENT = 50;
|
|
@@ -505,7 +462,7 @@ var isFile = async (path) => {
|
|
|
505
462
|
var findGitDir = async (start) => {
|
|
506
463
|
let current = resolve(start);
|
|
507
464
|
for (let depth = 0; depth < MAX_ASCENT; depth += 1) {
|
|
508
|
-
const candidate =
|
|
465
|
+
const candidate = join3(current, GIT_DIR);
|
|
509
466
|
if (await isDirectory(candidate)) {
|
|
510
467
|
return candidate;
|
|
511
468
|
}
|
|
@@ -549,7 +506,7 @@ var GitRepositoryInfoReader = class {
|
|
|
549
506
|
return void 0;
|
|
550
507
|
}
|
|
551
508
|
try {
|
|
552
|
-
const headPath =
|
|
509
|
+
const headPath = join3(gitDir, HEAD_FILE);
|
|
553
510
|
const content = await readFile(headPath, "utf8");
|
|
554
511
|
const branch = parseHead(content);
|
|
555
512
|
const root = dirname(gitDir);
|
|
@@ -560,32 +517,6 @@ var GitRepositoryInfoReader = class {
|
|
|
560
517
|
}
|
|
561
518
|
};
|
|
562
519
|
|
|
563
|
-
// src/infrastructure/process.ts
|
|
564
|
-
import { spawn as spawn2 } from "child_process";
|
|
565
|
-
var NodeProcessLauncher = class {
|
|
566
|
-
async launch(command, args, options) {
|
|
567
|
-
const detached = options?.detached ?? false;
|
|
568
|
-
await new Promise((resolve4, reject) => {
|
|
569
|
-
const child = spawn2(command, args, {
|
|
570
|
-
detached,
|
|
571
|
-
stdio: "ignore",
|
|
572
|
-
env: getMsysDisabledEnv()
|
|
573
|
-
});
|
|
574
|
-
const handleError = (error) => {
|
|
575
|
-
child.off("spawn", handleSpawn);
|
|
576
|
-
reject(error);
|
|
577
|
-
};
|
|
578
|
-
const handleSpawn = () => {
|
|
579
|
-
child.off("error", handleError);
|
|
580
|
-
child.unref();
|
|
581
|
-
resolve4();
|
|
582
|
-
};
|
|
583
|
-
child.once("error", handleError);
|
|
584
|
-
child.once("spawn", handleSpawn);
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
|
|
589
520
|
// src/infrastructure/terminalTheme.ts
|
|
590
521
|
import { createInterface } from "readline";
|
|
591
522
|
var parseOsc11Response = (response) => {
|
|
@@ -845,9 +776,9 @@ var MacUnityHubProjectsReader = class {
|
|
|
845
776
|
|
|
846
777
|
// src/infrastructure/unityhub.win.ts
|
|
847
778
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
848
|
-
import { basename as basename4, join as
|
|
849
|
-
var HUB_DIR =
|
|
850
|
-
var HUB_PROJECTS_PATH2 =
|
|
779
|
+
import { basename as basename4, join as join4 } from "path";
|
|
780
|
+
var HUB_DIR = join4(process.env.APPDATA ?? "", "UnityHub");
|
|
781
|
+
var HUB_PROJECTS_PATH2 = join4(HUB_DIR, "projects-v1.json");
|
|
851
782
|
var schemaVersion2 = "v1";
|
|
852
783
|
var toUnityProject2 = (entry) => {
|
|
853
784
|
const safePath = entry.path;
|
|
@@ -981,7 +912,7 @@ var WinUnityHubProjectsReader = class {
|
|
|
981
912
|
return newFavorite;
|
|
982
913
|
}
|
|
983
914
|
async readCliArgs(projectPath) {
|
|
984
|
-
const infoPath =
|
|
915
|
+
const infoPath = join4(HUB_DIR, "projectsInfo.json");
|
|
985
916
|
let content;
|
|
986
917
|
try {
|
|
987
918
|
content = await readFile3(infoPath, "utf8");
|
|
@@ -1008,9 +939,9 @@ var WinUnityHubProjectsReader = class {
|
|
|
1008
939
|
|
|
1009
940
|
// src/infrastructure/unityLock.ts
|
|
1010
941
|
import { execFile as execFile3 } from "child_process";
|
|
1011
|
-
import { constants as
|
|
1012
|
-
import { access as
|
|
1013
|
-
import { join as
|
|
942
|
+
import { constants as constants3 } from "fs";
|
|
943
|
+
import { access as access3, rm } from "fs/promises";
|
|
944
|
+
import { join as join5 } from "path";
|
|
1014
945
|
import { promisify as promisify3 } from "util";
|
|
1015
946
|
var execFileAsync3 = promisify3(execFile3);
|
|
1016
947
|
var buildBringToFrontScript = (pid) => {
|
|
@@ -1018,7 +949,7 @@ var buildBringToFrontScript = (pid) => {
|
|
|
1018
949
|
};
|
|
1019
950
|
var pathExists = async (target) => {
|
|
1020
951
|
try {
|
|
1021
|
-
await
|
|
952
|
+
await access3(target, constants3.F_OK);
|
|
1022
953
|
return true;
|
|
1023
954
|
} catch {
|
|
1024
955
|
return false;
|
|
@@ -1038,7 +969,7 @@ var UnityLockChecker = class {
|
|
|
1038
969
|
await this.bringUnityToFront(activeProcess.pid);
|
|
1039
970
|
return "skip";
|
|
1040
971
|
}
|
|
1041
|
-
const lockfilePath =
|
|
972
|
+
const lockfilePath = join5(projectPath, "Temp", "UnityLockfile");
|
|
1042
973
|
const hasLockfile = await pathExists(lockfilePath);
|
|
1043
974
|
if (!hasLockfile) {
|
|
1044
975
|
return "allow";
|
|
@@ -1076,7 +1007,7 @@ var UnityLockChecker = class {
|
|
|
1076
1007
|
};
|
|
1077
1008
|
var UnityLockStatusReader = class {
|
|
1078
1009
|
async isLocked(projectPath) {
|
|
1079
|
-
const lockfilePath =
|
|
1010
|
+
const lockfilePath = join5(projectPath, "Temp", "UnityLockfile");
|
|
1080
1011
|
return await pathExists(lockfilePath);
|
|
1081
1012
|
}
|
|
1082
1013
|
};
|
|
@@ -1443,11 +1374,11 @@ var WinUnityProcessTerminator = class {
|
|
|
1443
1374
|
|
|
1444
1375
|
// src/infrastructure/unityTemp.ts
|
|
1445
1376
|
import { rm as rm2 } from "fs/promises";
|
|
1446
|
-
import { join as
|
|
1377
|
+
import { join as join6 } from "path";
|
|
1447
1378
|
var TEMP_DIRECTORY_NAME = "Temp";
|
|
1448
1379
|
var UnityTempDirectoryCleaner = class {
|
|
1449
1380
|
async clean(projectPath) {
|
|
1450
|
-
const tempDirectoryPath =
|
|
1381
|
+
const tempDirectoryPath = join6(projectPath, TEMP_DIRECTORY_NAME);
|
|
1451
1382
|
try {
|
|
1452
1383
|
await rm2(tempDirectoryPath, {
|
|
1453
1384
|
recursive: true,
|
|
@@ -2762,7 +2693,7 @@ var SHELL_INIT_MARKER_START = "# >>> unity-hub-cli >>>";
|
|
|
2762
2693
|
var SHELL_INIT_MARKER_END = "# <<< unity-hub-cli <<<";
|
|
2763
2694
|
var getVersion = () => {
|
|
2764
2695
|
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
2765
|
-
const packageJsonPath =
|
|
2696
|
+
const packageJsonPath = join7(currentDir, "..", "package.json");
|
|
2766
2697
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2767
2698
|
return packageJson.version;
|
|
2768
2699
|
};
|
|
@@ -2770,18 +2701,18 @@ var getShellConfigPath = () => {
|
|
|
2770
2701
|
const shell = process3.env["SHELL"] ?? "";
|
|
2771
2702
|
const home = homedir();
|
|
2772
2703
|
if (shell.includes("zsh")) {
|
|
2773
|
-
return
|
|
2704
|
+
return join7(home, ".zshrc");
|
|
2774
2705
|
}
|
|
2775
2706
|
if (shell.includes("bash")) {
|
|
2776
|
-
const bashrcPath =
|
|
2777
|
-
const profilePath =
|
|
2707
|
+
const bashrcPath = join7(home, ".bashrc");
|
|
2708
|
+
const profilePath = join7(home, ".bash_profile");
|
|
2778
2709
|
return existsSync3(bashrcPath) ? bashrcPath : profilePath;
|
|
2779
2710
|
}
|
|
2780
2711
|
if (shell.includes("fish")) {
|
|
2781
|
-
return
|
|
2712
|
+
return join7(home, ".config", "fish", "config.fish");
|
|
2782
2713
|
}
|
|
2783
2714
|
if (process3.platform === "win32") {
|
|
2784
|
-
return
|
|
2715
|
+
return join7(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
2785
2716
|
}
|
|
2786
2717
|
return void 0;
|
|
2787
2718
|
};
|
|
@@ -3162,17 +3093,9 @@ var bootstrap = async () => {
|
|
|
3162
3093
|
lockStatusReader,
|
|
3163
3094
|
unityProcessReader
|
|
3164
3095
|
);
|
|
3165
|
-
const editorPathResolver = isWindows ? new WinEditorPathResolver() : new MacEditorPathResolver();
|
|
3166
|
-
const processLauncher = new NodeProcessLauncher();
|
|
3167
3096
|
const lockChecker = new UnityLockChecker(unityProcessReader, unityTempDirectoryCleaner);
|
|
3168
3097
|
const unityProcessTerminator = isWindows ? new WinUnityProcessTerminator() : new MacUnityProcessTerminator();
|
|
3169
|
-
const launchProjectUseCase = new LaunchProjectUseCase(
|
|
3170
|
-
editorPathResolver,
|
|
3171
|
-
processLauncher,
|
|
3172
|
-
unityHubReader,
|
|
3173
|
-
unityHubReader,
|
|
3174
|
-
lockChecker
|
|
3175
|
-
);
|
|
3098
|
+
const launchProjectUseCase = new LaunchProjectUseCase(lockChecker);
|
|
3176
3099
|
const terminateProjectUseCase = new TerminateProjectUseCase(
|
|
3177
3100
|
unityProcessReader,
|
|
3178
3101
|
unityProcessTerminator,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unity-hub-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "A CLI tool that reads Unity Hub's projects and launches Unity Editor with an interactive TUI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -38,9 +38,10 @@
|
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"chalk": "5.6.2",
|
|
41
|
-
"clipboardy": "5.0
|
|
42
|
-
"ink": "6.
|
|
43
|
-
"
|
|
41
|
+
"clipboardy": "5.2.0",
|
|
42
|
+
"ink": "6.6.0",
|
|
43
|
+
"launch-unity": "0.13.0",
|
|
44
|
+
"react": "19.2.4"
|
|
44
45
|
},
|
|
45
46
|
"engines": {
|
|
46
47
|
"node": ">=18"
|
|
@@ -55,20 +56,20 @@
|
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@eslint/js": "^9.39.1",
|
|
58
|
-
"@lavamoat/allow-scripts": "3.4.
|
|
59
|
-
"@types/node": "
|
|
60
|
-
"@types/react": "19.2.
|
|
61
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
62
|
-
"@typescript-eslint/parser": "8.
|
|
63
|
-
"eslint": "9.39.
|
|
59
|
+
"@lavamoat/allow-scripts": "3.4.2",
|
|
60
|
+
"@types/node": "25.2.0",
|
|
61
|
+
"@types/react": "19.2.10",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "8.54.0",
|
|
63
|
+
"@typescript-eslint/parser": "8.54.0",
|
|
64
|
+
"eslint": "9.39.2",
|
|
64
65
|
"eslint-config-prettier": "10.1.8",
|
|
65
66
|
"eslint-import-resolver-typescript": "4.4.4",
|
|
66
67
|
"eslint-plugin-import": "2.32.0",
|
|
67
|
-
"prettier": "3.
|
|
68
|
+
"prettier": "3.8.1",
|
|
68
69
|
"tsup": "8.5.1",
|
|
69
70
|
"tsx": "4.21.0",
|
|
70
71
|
"typescript": "5.9.3",
|
|
71
72
|
"typescript-eslint": "^8.48.1",
|
|
72
|
-
"vitest": "4.0.
|
|
73
|
+
"vitest": "4.0.18"
|
|
73
74
|
}
|
|
74
75
|
}
|