unity-hub-cli 0.21.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.
Files changed (2) hide show
  1. package/dist/index.js +53 -130
  2. 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 join9 } from "path";
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(editorPathResolver, processLauncher, unityHubProjectsReader, unityProjectOptionsReader, unityProcessLockChecker) {
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
- const editorPath = await this.editorPathResolver.resolve(project.version);
73
- const extraArgs = await this.unityProjectOptionsReader.readCliArgs(project.path);
74
- const launchArgs = ["-projectPath", project.path, ...extraArgs];
75
- await this.processLauncher.launch(editorPath, launchArgs, {
76
- detached: true
82
+ await launchUnitySilently({
83
+ projectPath: project.path,
84
+ unityArgs: [],
85
+ unityVersion: project.version.value
77
86
  });
78
- await this.unityHubProjectsReader.updateLastModified(project.path, /* @__PURE__ */ new Date());
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 as constants3, existsSync } from "fs";
273
- import { access as access3 } from "fs/promises";
274
- import { basename, join as join3 } from "path";
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 access3(configuredPath, constants3.F_OK);
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 = join3(projectRoot, `${projectName}.sln`);
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 constants4, existsSync as existsSync2 } from "fs";
333
- import { access as access4 } from "fs/promises";
334
- import { basename as basename2, join as join4 } from "path";
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 access4(configuredPath, constants4.F_OK);
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 = join4(projectRoot, `${projectName}.sln`);
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 join5, resolve } from "path";
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 = join5(current, GIT_DIR);
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 = join5(gitDir, HEAD_FILE);
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 join6 } from "path";
849
- var HUB_DIR = join6(process.env.APPDATA ?? "", "UnityHub");
850
- var HUB_PROJECTS_PATH2 = join6(HUB_DIR, "projects-v1.json");
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 = join6(HUB_DIR, "projectsInfo.json");
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 constants5 } from "fs";
1012
- import { access as access5, rm } from "fs/promises";
1013
- import { join as join7 } from "path";
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 access5(target, constants5.F_OK);
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 = join7(projectPath, "Temp", "UnityLockfile");
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 = join7(projectPath, "Temp", "UnityLockfile");
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 join8 } from "path";
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 = join8(projectPath, TEMP_DIRECTORY_NAME);
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 = join9(currentDir, "..", "package.json");
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 join9(home, ".zshrc");
2704
+ return join7(home, ".zshrc");
2774
2705
  }
2775
2706
  if (shell.includes("bash")) {
2776
- const bashrcPath = join9(home, ".bashrc");
2777
- const profilePath = join9(home, ".bash_profile");
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 join9(home, ".config", "fish", "config.fish");
2712
+ return join7(home, ".config", "fish", "config.fish");
2782
2713
  }
2783
2714
  if (process3.platform === "win32") {
2784
- return join9(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
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.21.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.1",
42
- "ink": "6.5.1",
43
- "react": "19.2.1"
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.1",
59
- "@types/node": "24.10.1",
60
- "@types/react": "19.2.7",
61
- "@typescript-eslint/eslint-plugin": "8.48.1",
62
- "@typescript-eslint/parser": "8.48.1",
63
- "eslint": "9.39.1",
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.7.4",
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.15"
73
+ "vitest": "4.0.18"
73
74
  }
74
75
  }