launch-unity 0.14.0 → 0.15.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.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  * Exports core functions for programmatic usage.
4
4
  * Uses lib.ts which has no CLI side effects.
5
5
  */
6
- export { LaunchOptions, LaunchResolvedOptions, UnityProcessInfo, parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
6
+ export { LaunchOptions, LaunchResolvedOptions, UnityProcessInfo, OrchestrateOptions, OrchestrateResult, parseArgs, findUnityProjectBfs, getUnityVersion, launch, orchestrateLaunch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAEhB,SAAS,EACT,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EAEjB,SAAS,EACT,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -5,4 +5,4 @@
5
5
  */
6
6
  export {
7
7
  // Functions
8
- parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
8
+ parseArgs, findUnityProjectBfs, getUnityVersion, launch, orchestrateLaunch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
package/dist/launch.d.ts CHANGED
@@ -1,32 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export type LaunchOptions = {
3
- subcommand?: "update";
4
- projectPath?: string;
5
- platform?: string | undefined;
6
- unityArgs: string[];
7
- searchMaxDepth: number;
8
- restart: boolean;
9
- quit: boolean;
10
- addUnityHub: boolean;
11
- favoriteUnityHub: boolean;
12
- };
13
- export type LaunchResolvedOptions = {
14
- projectPath: string;
15
- platform?: string | undefined;
16
- unityArgs: string[];
17
- unityVersion: string;
18
- };
19
- export type UnityProcessInfo = {
20
- pid: number;
21
- projectPath: string;
22
- };
23
- export declare function parseArgs(argv: string[]): LaunchOptions;
24
- export declare function getUnityVersion(projectPath: string): string;
25
- export declare function findRunningUnityProcess(projectPath: string): Promise<UnityProcessInfo | undefined>;
26
- export declare function focusUnityProcess(pid: number): Promise<void>;
27
- export declare function handleStaleLockfile(projectPath: string): Promise<void>;
28
- export declare function killRunningUnity(projectPath: string): Promise<void>;
29
- export declare function quitRunningUnity(projectPath: string): Promise<void>;
30
- export declare function findUnityProjectBfs(rootDir: string, maxDepth: number): string | undefined;
31
- export declare function launch(opts: LaunchResolvedOptions): Promise<void>;
2
+ export {};
32
3
  //# sourceMappingURL=launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":";AAeA,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAyJF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2HvD;AA0CD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAe3D;AA2ND,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAIxG;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;AA4CD,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B5E;AAkCD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBzE;AA0CD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BzE;AAgDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqDzF;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvE"}
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":""}
package/dist/launch.js CHANGED
@@ -3,22 +3,11 @@
3
3
  launch-unity: Open a Unity project with the matching Editor version.
4
4
  Platforms: macOS, Windows
5
5
  */
6
- import { execFile, spawn } from "node:child_process";
7
- import { existsSync, readFileSync, readdirSync, lstatSync, realpathSync } from "node:fs";
8
- import { rm } from "node:fs/promises";
6
+ import { spawn } from "node:child_process";
7
+ import { readFileSync, realpathSync } from "node:fs";
9
8
  import { dirname, join, resolve } from "node:path";
10
9
  import { fileURLToPath, pathToFileURL } from "node:url";
11
- import { promisify } from "node:util";
12
- import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, groupCliArgs } from "./unityHub.js";
13
- const execFileAsync = promisify(execFile);
14
- const UNITY_EXECUTABLE_PATTERN_MAC = /Unity\.app\/Contents\/MacOS\/Unity/i;
15
- const UNITY_EXECUTABLE_PATTERN_WINDOWS = /Unity\.exe/i;
16
- const PROJECT_PATH_PATTERN = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
17
- const PROCESS_LIST_COMMAND_MAC = "ps";
18
- const PROCESS_LIST_ARGS_MAC = ["-axo", "pid=,command=", "-ww"];
19
- const WINDOWS_POWERSHELL = "powershell";
20
- const UNITY_LOCKFILE_NAME = "UnityLockfile";
21
- const TEMP_DIRECTORY_NAME = "Temp";
10
+ import { orchestrateLaunch } from "./lib.js";
22
11
  const npmExecutableName = () => {
23
12
  return process.platform === "win32" ? "npm.cmd" : "npm";
24
13
  };
@@ -130,7 +119,7 @@ const compareSemverTriplet = (left, right) => {
130
119
  }
131
120
  return 0;
132
121
  };
133
- export function parseArgs(argv) {
122
+ function parseArgs(argv) {
134
123
  const args = argv.slice(2);
135
124
  const doubleDashIndex = args.indexOf("--");
136
125
  let cliArgs = doubleDashIndex >= 0 ? args.slice(0, doubleDashIndex) : args;
@@ -284,618 +273,25 @@ Commands:
284
273
  `;
285
274
  process.stdout.write(help);
286
275
  }
287
- export function getUnityVersion(projectPath) {
288
- const versionFile = join(projectPath, "ProjectSettings", "ProjectVersion.txt");
289
- if (!existsSync(versionFile)) {
290
- console.error(`Error: ProjectVersion.txt not found at ${versionFile}`);
291
- console.error("This does not appear to be a Unity project.");
292
- process.exit(1);
293
- }
294
- const content = readFileSync(versionFile, "utf8");
295
- const version = content.match(/m_EditorVersion:\s*([^\s\n]+)/)?.[1];
296
- if (!version) {
297
- console.error(`Error: Could not extract Unity version from ${versionFile}`);
298
- process.exit(1);
299
- }
300
- return version;
301
- }
302
- function getUnityPathWindows(version) {
303
- const candidates = [];
304
- const programFiles = process.env["PROGRAMFILES"];
305
- const programFilesX86 = process.env["PROGRAMFILES(X86)"];
306
- const localAppData = process.env["LOCALAPPDATA"];
307
- const addCandidate = (base) => {
308
- if (!base) {
309
- return;
310
- }
311
- candidates.push(join(base, "Unity", "Hub", "Editor", version, "Editor", "Unity.exe"));
312
- };
313
- addCandidate(programFiles);
314
- addCandidate(programFilesX86);
315
- addCandidate(localAppData);
316
- candidates.push(join("C:\\", "Program Files", "Unity", "Hub", "Editor", version, "Editor", "Unity.exe"));
317
- for (const candidate of candidates) {
318
- if (existsSync(candidate)) {
319
- return candidate;
320
- }
321
- }
322
- return candidates[0] ?? join("C:\\", "Program Files", "Unity", "Hub", "Editor", version, "Editor", "Unity.exe");
323
- }
324
- function getUnityPath(version) {
325
- if (process.platform === "darwin") {
326
- return `/Applications/Unity/Hub/Editor/${version}/Unity.app/Contents/MacOS/Unity`;
327
- }
328
- if (process.platform === "win32") {
329
- return getUnityPathWindows(version);
330
- }
331
- return `/Applications/Unity/Hub/Editor/${version}/Unity.app/Contents/MacOS/Unity`;
332
- }
333
- function ensureProjectPath(projectPath) {
334
- if (!existsSync(projectPath)) {
335
- console.error(`Error: Project directory not found: ${projectPath}`);
336
- process.exit(1);
337
- }
338
- }
339
- const removeTrailingSeparators = (target) => {
340
- let trimmed = target;
341
- while (trimmed.length > 1 && (trimmed.endsWith("/") || trimmed.endsWith("\\"))) {
342
- trimmed = trimmed.slice(0, -1);
343
- }
344
- return trimmed;
345
- };
346
- const normalizePath = (target) => {
347
- const resolvedPath = resolve(target);
348
- const trimmed = removeTrailingSeparators(resolvedPath);
349
- return trimmed;
350
- };
351
- const toComparablePath = (value) => {
352
- return value.replace(/\\/g, "/").toLocaleLowerCase();
353
- };
354
- const pathsEqual = (left, right) => {
355
- return toComparablePath(normalizePath(left)) === toComparablePath(normalizePath(right));
356
- };
357
- function extractProjectPath(command) {
358
- const match = command.match(PROJECT_PATH_PATTERN);
359
- if (!match) {
360
- return undefined;
361
- }
362
- const raw = match[1];
363
- if (!raw) {
364
- return undefined;
365
- }
366
- const trimmed = raw.trim();
367
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
368
- return trimmed.slice(1, -1);
369
- }
370
- if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
371
- return trimmed.slice(1, -1);
372
- }
373
- return trimmed;
374
- }
375
- const isUnityAuxiliaryProcess = (command) => {
376
- const normalizedCommand = command.toLowerCase();
377
- if (normalizedCommand.includes("-batchmode")) {
378
- return true;
379
- }
380
- return normalizedCommand.includes("assetimportworker");
381
- };
382
- async function listUnityProcessesMac() {
383
- let stdout = "";
384
- try {
385
- const result = await execFileAsync(PROCESS_LIST_COMMAND_MAC, PROCESS_LIST_ARGS_MAC);
386
- stdout = result.stdout;
387
- }
388
- catch (error) {
389
- const message = error instanceof Error ? error.message : String(error);
390
- console.error(`Failed to retrieve Unity process list: ${message}`);
391
- return [];
392
- }
393
- const lines = stdout
394
- .split("\n")
395
- .map((line) => line.trim())
396
- .filter((line) => line.length > 0);
397
- const processes = [];
398
- for (const line of lines) {
399
- const match = line.match(/^(\d+)\s+(.*)$/);
400
- if (!match) {
401
- continue;
402
- }
403
- const pidValue = Number.parseInt(match[1] ?? "", 10);
404
- if (!Number.isFinite(pidValue)) {
405
- continue;
406
- }
407
- const command = match[2] ?? "";
408
- if (!UNITY_EXECUTABLE_PATTERN_MAC.test(command)) {
409
- continue;
410
- }
411
- if (isUnityAuxiliaryProcess(command)) {
412
- continue;
413
- }
414
- const projectArgument = extractProjectPath(command);
415
- if (!projectArgument) {
416
- continue;
417
- }
418
- processes.push({
419
- pid: pidValue,
420
- projectPath: normalizePath(projectArgument),
421
- });
422
- }
423
- return processes;
424
- }
425
- async function listUnityProcessesWindows() {
426
- const scriptLines = [
427
- "$ErrorActionPreference = 'Stop'",
428
- "$processes = Get-CimInstance Win32_Process -Filter \"Name = 'Unity.exe'\" | Where-Object { $_.CommandLine }",
429
- "foreach ($process in $processes) {",
430
- " $commandLine = $process.CommandLine -replace \"`r\", ' ' -replace \"`n\", ' '",
431
- " Write-Output (\"{0}|{1}\" -f $process.ProcessId, $commandLine)",
432
- "}",
433
- ];
434
- let stdout = "";
435
- try {
436
- const result = await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
437
- stdout = result.stdout ?? "";
438
- }
439
- catch (error) {
440
- const message = error instanceof Error ? error.message : String(error);
441
- console.error(`Failed to retrieve Unity process list on Windows: ${message}`);
442
- return [];
443
- }
444
- const lines = stdout
445
- .split("\n")
446
- .map((line) => line.trim())
447
- .filter((line) => line.length > 0);
448
- const processes = [];
449
- for (const line of lines) {
450
- const delimiterIndex = line.indexOf("|");
451
- if (delimiterIndex < 0) {
452
- continue;
453
- }
454
- const pidText = line.slice(0, delimiterIndex).trim();
455
- const command = line.slice(delimiterIndex + 1).trim();
456
- const pidValue = Number.parseInt(pidText, 10);
457
- if (!Number.isFinite(pidValue)) {
458
- continue;
459
- }
460
- if (!UNITY_EXECUTABLE_PATTERN_WINDOWS.test(command)) {
461
- continue;
462
- }
463
- if (isUnityAuxiliaryProcess(command)) {
464
- continue;
465
- }
466
- const projectArgument = extractProjectPath(command);
467
- if (!projectArgument) {
468
- continue;
469
- }
470
- processes.push({
471
- pid: pidValue,
472
- projectPath: normalizePath(projectArgument),
473
- });
474
- }
475
- return processes;
476
- }
477
- async function listUnityProcesses() {
478
- if (process.platform === "darwin") {
479
- return await listUnityProcessesMac();
480
- }
481
- if (process.platform === "win32") {
482
- return await listUnityProcessesWindows();
483
- }
484
- return [];
485
- }
486
- export async function findRunningUnityProcess(projectPath) {
487
- const normalizedTarget = normalizePath(projectPath);
488
- const processes = await listUnityProcesses();
489
- return processes.find((candidate) => pathsEqual(candidate.projectPath, normalizedTarget));
490
- }
491
- export async function focusUnityProcess(pid) {
492
- if (process.platform === "darwin") {
493
- await focusUnityProcessMac(pid);
494
- return;
495
- }
496
- if (process.platform === "win32") {
497
- await focusUnityProcessWindows(pid);
498
- }
499
- }
500
- async function focusUnityProcessMac(pid) {
501
- const script = `tell application "System Events" to set frontmost of (first process whose unix id is ${pid}) to true`;
502
- try {
503
- await execFileAsync("osascript", ["-e", script]);
504
- console.log("Brought existing Unity to the front.");
505
- }
506
- catch (error) {
507
- const message = error instanceof Error ? error.message : String(error);
508
- console.warn(`Failed to bring Unity to front: ${message}`);
509
- }
510
- }
511
- async function focusUnityProcessWindows(pid) {
512
- const addTypeLines = [
513
- "Add-Type -TypeDefinition @\"",
514
- "using System;",
515
- "using System.Runtime.InteropServices;",
516
- "public static class Win32Interop {",
517
- " [DllImport(\"user32.dll\")] public static extern bool SetForegroundWindow(IntPtr hWnd);",
518
- " [DllImport(\"user32.dll\")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);",
519
- "}",
520
- "\"@",
521
- ];
522
- const scriptLines = [
523
- "$ErrorActionPreference = 'Stop'",
524
- ...addTypeLines,
525
- `try { $process = Get-Process -Id ${pid} -ErrorAction Stop } catch { return }`,
526
- "$handle = $process.MainWindowHandle",
527
- "if ($handle -eq 0) { return }",
528
- "[Win32Interop]::ShowWindowAsync($handle, 9) | Out-Null",
529
- "[Win32Interop]::SetForegroundWindow($handle) | Out-Null",
530
- ];
531
- try {
532
- await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
533
- console.log("Brought existing Unity to the front.");
534
- }
535
- catch (error) {
536
- const message = error instanceof Error ? error.message : String(error);
537
- console.warn(`Failed to bring Unity to front on Windows: ${message}`);
538
- }
539
- }
540
- export async function handleStaleLockfile(projectPath) {
541
- const tempDirectoryPath = join(projectPath, TEMP_DIRECTORY_NAME);
542
- const lockfilePath = join(tempDirectoryPath, UNITY_LOCKFILE_NAME);
543
- if (!existsSync(lockfilePath)) {
544
- return;
545
- }
546
- console.log(`UnityLockfile found without active Unity process: ${lockfilePath}`);
547
- console.log("Assuming previous crash. Cleaning Temp directory and continuing launch.");
548
- try {
549
- await rm(tempDirectoryPath, { recursive: true, force: true });
550
- console.log("Deleted Temp directory.");
551
- }
552
- catch (error) {
553
- const message = error instanceof Error ? error.message : String(error);
554
- console.warn(`Failed to delete Temp directory: ${message}`);
555
- }
556
- try {
557
- await rm(lockfilePath, { force: true });
558
- console.log("Deleted UnityLockfile.");
559
- }
560
- catch (error) {
561
- const message = error instanceof Error ? error.message : String(error);
562
- console.warn(`Failed to delete UnityLockfile: ${message}`);
563
- }
564
- console.log();
565
- }
566
- const KILL_POLL_INTERVAL_MS = 100;
567
- const KILL_TIMEOUT_MS = 10000;
568
- const GRACEFUL_QUIT_TIMEOUT_MS = 10000;
569
- function isProcessAlive(pid) {
570
- try {
571
- process.kill(pid, 0);
572
- return true;
573
- }
574
- catch {
575
- return false;
576
- }
577
- }
578
- function killProcess(pid) {
579
- try {
580
- process.kill(pid, "SIGKILL");
581
- }
582
- catch {
583
- // Process already exited
584
- }
585
- }
586
- async function waitForProcessExit(pid, timeoutMs) {
587
- const start = Date.now();
588
- while (Date.now() - start < timeoutMs) {
589
- if (!isProcessAlive(pid)) {
590
- return true;
591
- }
592
- await new Promise((resolve) => setTimeout(resolve, KILL_POLL_INTERVAL_MS));
593
- }
594
- return false;
595
- }
596
- export async function killRunningUnity(projectPath) {
597
- const processInfo = await findRunningUnityProcess(projectPath);
598
- if (!processInfo) {
599
- console.log("No running Unity process found for this project.");
600
- console.log();
601
- return;
602
- }
603
- const pid = processInfo.pid;
604
- console.log(`Killing Unity (PID: ${pid})...`);
605
- killProcess(pid);
606
- const exited = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
607
- if (!exited) {
608
- console.error(`Error: Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
609
- process.exit(1);
610
- }
611
- console.log("Unity killed.");
612
- console.log();
613
- }
614
- async function sendGracefulQuitMac(pid) {
615
- // System Events quit and "tell application to quit" leave UnityLockfile behind,
616
- // so we send Cmd+Q keystroke to trigger Unity's normal user-initiated shutdown flow
617
- const script = [
618
- 'tell application "System Events"',
619
- ` set frontmost of (first process whose unix id is ${pid}) to true`,
620
- ' keystroke "q" using {command down}',
621
- "end tell",
622
- ].join("\n");
623
- try {
624
- await execFileAsync("osascript", ["-e", script]);
625
- }
626
- catch {
627
- // Process may have already exited
628
- }
629
- }
630
- async function sendGracefulQuitWindows(pid) {
631
- // process.kill(pid, "SIGTERM") forcefully kills on Windows, so use CloseMainWindow() to send WM_CLOSE
632
- const scriptLines = [
633
- "$ErrorActionPreference = 'Stop'",
634
- `try { $proc = Get-Process -Id ${pid} -ErrorAction Stop; $proc.CloseMainWindow() | Out-Null } catch { }`,
635
- ];
636
- try {
637
- await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
638
- }
639
- catch {
640
- // Process may have already exited
641
- }
642
- }
643
- async function sendGracefulQuit(pid) {
644
- if (process.platform === "darwin") {
645
- await sendGracefulQuitMac(pid);
646
- return;
647
- }
648
- if (process.platform === "win32") {
649
- await sendGracefulQuitWindows(pid);
650
- return;
651
- }
652
- }
653
- export async function quitRunningUnity(projectPath) {
654
- const processInfo = await findRunningUnityProcess(projectPath);
655
- if (!processInfo) {
656
- console.log("No running Unity process found for this project.");
657
- return;
658
- }
659
- const pid = processInfo.pid;
660
- console.log(`Quitting Unity (PID: ${pid})...`);
661
- await sendGracefulQuit(pid);
662
- console.log(`Sent graceful quit signal. Waiting up to ${GRACEFUL_QUIT_TIMEOUT_MS / 1000}s...`);
663
- const exitedGracefully = await waitForProcessExit(pid, GRACEFUL_QUIT_TIMEOUT_MS);
664
- if (exitedGracefully) {
665
- console.log("Unity quit gracefully.");
666
- return;
667
- }
668
- console.log("Unity did not respond to graceful quit. Force killing...");
669
- killProcess(pid);
670
- const exitedAfterKill = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
671
- if (!exitedAfterKill) {
672
- console.error(`Error: Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
673
- process.exit(1);
674
- }
675
- console.log("Unity force killed.");
676
- }
677
- function hasBuildTargetArg(unityArgs) {
678
- for (const arg of unityArgs) {
679
- if (arg === "-buildTarget") {
680
- return true;
681
- }
682
- if (arg.startsWith("-buildTarget=")) {
683
- return true;
684
- }
685
- }
686
- return false;
687
- }
688
- const EXCLUDED_DIR_NAMES = new Set([
689
- "library",
690
- "temp",
691
- "logs",
692
- "obj",
693
- ".git",
694
- "node_modules",
695
- ".idea",
696
- ".vscode",
697
- ".vs",
698
- ]);
699
- function isUnityProjectRoot(candidateDir) {
700
- const versionFile = join(candidateDir, "ProjectSettings", "ProjectVersion.txt");
701
- return existsSync(versionFile);
702
- }
703
- function listSubdirectoriesSorted(dir) {
704
- let entries = [];
705
- try {
706
- const dirents = readdirSync(dir, { withFileTypes: true });
707
- const subdirs = dirents
708
- .filter((d) => d.isDirectory())
709
- .map((d) => d.name)
710
- .filter((name) => !EXCLUDED_DIR_NAMES.has(name.toLocaleLowerCase()));
711
- subdirs.sort((a, b) => a.localeCompare(b));
712
- entries = subdirs.map((name) => join(dir, name));
713
- }
714
- catch {
715
- // Ignore directories we cannot read
716
- entries = [];
717
- }
718
- return entries;
719
- }
720
- export function findUnityProjectBfs(rootDir, maxDepth) {
721
- const queue = [];
722
- let rootCanonical;
723
- try {
724
- rootCanonical = realpathSync(rootDir);
725
- }
726
- catch {
727
- rootCanonical = rootDir;
728
- }
729
- queue.push({ dir: rootCanonical, depth: 0 });
730
- const visited = new Set([toComparablePath(normalizePath(rootCanonical))]);
731
- while (queue.length > 0) {
732
- const current = queue.shift();
733
- if (!current) {
734
- continue;
735
- }
736
- const { dir, depth } = current;
737
- if (isUnityProjectRoot(dir)) {
738
- return normalizePath(dir);
739
- }
740
- const canDescend = maxDepth === -1 || depth < maxDepth;
741
- if (!canDescend) {
742
- continue;
743
- }
744
- const children = listSubdirectoriesSorted(dir);
745
- for (const child of children) {
746
- let childCanonical = child;
747
- try {
748
- const stat = lstatSync(child);
749
- if (stat.isSymbolicLink()) {
750
- try {
751
- childCanonical = realpathSync(child);
752
- }
753
- catch {
754
- // Broken symlink: skip
755
- continue;
756
- }
757
- }
758
- }
759
- catch {
760
- continue;
761
- }
762
- const key = toComparablePath(normalizePath(childCanonical));
763
- if (visited.has(key)) {
764
- continue;
765
- }
766
- visited.add(key);
767
- queue.push({ dir: childCanonical, depth: depth + 1 });
768
- }
769
- }
770
- return undefined;
771
- }
772
- export async function launch(opts) {
773
- const { projectPath, platform, unityArgs, unityVersion } = opts;
774
- const unityPath = getUnityPath(unityVersion);
775
- if (!existsSync(unityPath)) {
776
- console.error(`Error: Unity ${unityVersion} not found at ${unityPath}`);
777
- console.error("Please install Unity through Unity Hub.");
778
- process.exit(1);
779
- }
780
- console.log("Opening Unity...");
781
- console.log(`Project Path: ${projectPath}`);
782
- console.log(`Detected Unity version: ${unityVersion}`);
783
- const args = ["-projectPath", projectPath];
784
- const unityArgsContainBuildTarget = hasBuildTargetArg(unityArgs);
785
- if (platform && platform.length > 0 && !unityArgsContainBuildTarget) {
786
- args.push("-buildTarget", platform);
787
- }
788
- const hubCliArgs = await getProjectCliArgs(projectPath);
789
- if (hubCliArgs.length > 0) {
790
- console.log("Unity Hub launch options:");
791
- for (const line of groupCliArgs(hubCliArgs)) {
792
- console.log(` ${line}`);
793
- }
794
- args.push(...hubCliArgs);
795
- }
796
- else {
797
- console.log("Unity Hub launch options: none");
798
- }
799
- if (unityArgs.length > 0) {
800
- args.push(...unityArgs);
801
- }
802
- return new Promise((resolve, reject) => {
803
- const child = spawn(unityPath, args, {
804
- stdio: "ignore",
805
- detached: true,
806
- // Git Bash (MSYS) がWindows パスをUnix 形式に自動変換するのを防ぐ
807
- env: {
808
- ...process.env,
809
- MSYS_NO_PATHCONV: "1",
810
- },
811
- });
812
- const handleError = (error) => {
813
- child.removeListener("spawn", handleSpawn);
814
- reject(new Error(`Failed to launch Unity: ${error.message}`));
815
- };
816
- const handleSpawn = () => {
817
- child.removeListener("error", handleError);
818
- child.unref();
819
- resolve();
820
- };
821
- child.once("error", handleError);
822
- child.once("spawn", handleSpawn);
823
- });
824
- }
825
276
  async function main() {
826
277
  const options = parseArgs(process.argv);
827
278
  if (options.subcommand === "update") {
828
279
  await runSelfUpdate();
829
280
  return;
830
281
  }
831
- let resolvedProjectPath = options.projectPath;
832
- if (!resolvedProjectPath) {
833
- const searchRoot = process.cwd();
834
- const depthInfo = options.searchMaxDepth === -1 ? "unlimited" : String(options.searchMaxDepth);
835
- console.log(`Searching for Unity project under ${searchRoot} (max-depth: ${depthInfo})...`);
836
- const found = findUnityProjectBfs(searchRoot, options.searchMaxDepth);
837
- if (!found) {
838
- console.error(`Error: Unity project not found under ${searchRoot}.`);
839
- process.exit(1);
840
- return;
841
- }
842
- console.log();
843
- resolvedProjectPath = found;
844
- }
845
- ensureProjectPath(resolvedProjectPath);
846
- const unityVersion = getUnityVersion(resolvedProjectPath);
847
- // Unity Hub only mode: -a or -f flags skip launching Unity
848
- const unityHubOnlyMode = options.addUnityHub || options.favoriteUnityHub;
849
- if (unityHubOnlyMode) {
850
- console.log(`Detected Unity version: ${unityVersion}`);
851
- console.log(`Project Path: ${resolvedProjectPath}`);
852
- const now = new Date();
853
- try {
854
- await ensureProjectEntryAndUpdate(resolvedProjectPath, unityVersion, now, options.favoriteUnityHub);
855
- console.log("Unity Hub entry updated.");
856
- }
857
- catch (error) {
858
- const message = error instanceof Error ? error.message : String(error);
859
- console.warn(`Failed to update Unity Hub: ${message}`);
860
- }
861
- return;
862
- }
863
- if (options.quit && options.restart) {
864
- console.error("Error: --quit and --restart cannot be used together.");
865
- process.exit(1);
866
- }
867
- if (options.quit) {
868
- await quitRunningUnity(resolvedProjectPath);
869
- return;
870
- }
871
- if (options.restart) {
872
- await killRunningUnity(resolvedProjectPath);
873
- }
874
- else {
875
- const runningProcess = await findRunningUnityProcess(resolvedProjectPath);
876
- if (runningProcess) {
877
- console.log(`Unity process already running for project: ${resolvedProjectPath} (PID: ${runningProcess.pid})`);
878
- await focusUnityProcess(runningProcess.pid);
879
- process.exit(0);
880
- return;
881
- }
882
- }
883
- await handleStaleLockfile(resolvedProjectPath);
884
- const resolved = {
885
- projectPath: resolvedProjectPath,
282
+ const result = await orchestrateLaunch({
283
+ projectPath: options.projectPath,
284
+ searchRoot: process.cwd(),
285
+ searchMaxDepth: options.searchMaxDepth,
886
286
  platform: options.platform,
887
287
  unityArgs: options.unityArgs,
888
- unityVersion,
889
- };
890
- await launch(resolved);
891
- // Best-effort update of Unity Hub's lastModified timestamp.
892
- const now = new Date();
893
- try {
894
- await updateLastModifiedIfExists(resolvedProjectPath, now);
895
- }
896
- catch (error) {
897
- const message = error instanceof Error ? error.message : String(error);
898
- console.warn(`Failed to update Unity Hub lastModified: ${message}`);
288
+ restart: options.restart,
289
+ quit: options.quit,
290
+ addUnityHub: options.addUnityHub,
291
+ favoriteUnityHub: options.favoriteUnityHub,
292
+ });
293
+ if (result.action === "focused") {
294
+ process.exit(0);
899
295
  }
900
296
  }
901
297
  async function runSelfUpdate() {
package/dist/lib.d.ts CHANGED
@@ -33,5 +33,37 @@ export declare function killRunningUnity(projectPath: string): Promise<void>;
33
33
  export declare function quitRunningUnity(projectPath: string): Promise<void>;
34
34
  export declare function findUnityProjectBfs(rootDir: string, maxDepth: number): string | undefined;
35
35
  export declare function launch(opts: LaunchResolvedOptions): Promise<void>;
36
+ export type OrchestrateOptions = {
37
+ projectPath?: string | undefined;
38
+ searchRoot: string;
39
+ searchMaxDepth: number;
40
+ platform?: string | undefined;
41
+ unityArgs: string[];
42
+ restart: boolean;
43
+ quit: boolean;
44
+ addUnityHub: boolean;
45
+ favoriteUnityHub: boolean;
46
+ };
47
+ export type OrchestrateResult = {
48
+ action: "launched";
49
+ projectPath: string;
50
+ unityVersion: string;
51
+ } | {
52
+ action: "focused";
53
+ projectPath: string;
54
+ pid: number;
55
+ } | {
56
+ action: "quit";
57
+ projectPath: string;
58
+ } | {
59
+ action: "killed-and-launched";
60
+ projectPath: string;
61
+ unityVersion: string;
62
+ } | {
63
+ action: "hub-updated";
64
+ projectPath: string;
65
+ unityVersion: string;
66
+ };
67
+ export declare function orchestrateLaunch(options: OrchestrateOptions): Promise<OrchestrateResult>;
36
68
  export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs };
37
69
  //# sourceMappingURL=lib.d.ts.map
package/dist/lib.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvI,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAYF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAyHvD;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAc3D;AAoND,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAIxG;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;AA4CD,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B5E;AAkCD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBzE;AA0CD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BzE;AAgDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqDzF;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvE;AAGD,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"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"}
package/dist/lib.js CHANGED
@@ -656,5 +656,68 @@ export async function launch(opts) {
656
656
  child.once("spawn", handleSpawn);
657
657
  });
658
658
  }
659
+ export async function orchestrateLaunch(options) {
660
+ if (options.quit && options.restart) {
661
+ throw new Error("--quit and --restart cannot be used together.");
662
+ }
663
+ let resolvedProjectPath = options.projectPath;
664
+ if (!resolvedProjectPath) {
665
+ const depthInfo = options.searchMaxDepth === -1 ? "unlimited" : String(options.searchMaxDepth);
666
+ console.log(`Searching for Unity project under ${options.searchRoot} (max-depth: ${depthInfo})...`);
667
+ const found = findUnityProjectBfs(options.searchRoot, options.searchMaxDepth);
668
+ if (!found) {
669
+ throw new Error(`Unity project not found under ${options.searchRoot}.`);
670
+ }
671
+ console.log();
672
+ resolvedProjectPath = found;
673
+ }
674
+ if (!existsSync(resolvedProjectPath)) {
675
+ throw new Error(`Project directory not found: ${resolvedProjectPath}`);
676
+ }
677
+ const unityVersion = getUnityVersion(resolvedProjectPath);
678
+ if (options.addUnityHub || options.favoriteUnityHub) {
679
+ console.log(`Detected Unity version: ${unityVersion}`);
680
+ console.log(`Project Path: ${resolvedProjectPath}`);
681
+ const now = new Date();
682
+ await ensureProjectEntryAndUpdate(resolvedProjectPath, unityVersion, now, options.favoriteUnityHub);
683
+ console.log("Unity Hub entry updated.");
684
+ return { action: "hub-updated", projectPath: resolvedProjectPath, unityVersion };
685
+ }
686
+ if (options.quit) {
687
+ await quitRunningUnity(resolvedProjectPath);
688
+ return { action: "quit", projectPath: resolvedProjectPath };
689
+ }
690
+ const isRestart = options.restart;
691
+ if (isRestart) {
692
+ await killRunningUnity(resolvedProjectPath);
693
+ }
694
+ else {
695
+ const runningProcess = await findRunningUnityProcess(resolvedProjectPath);
696
+ if (runningProcess) {
697
+ console.log(`Unity process already running for project: ${resolvedProjectPath} (PID: ${runningProcess.pid})`);
698
+ await focusUnityProcess(runningProcess.pid);
699
+ return { action: "focused", projectPath: resolvedProjectPath, pid: runningProcess.pid };
700
+ }
701
+ }
702
+ await handleStaleLockfile(resolvedProjectPath);
703
+ const resolved = {
704
+ projectPath: resolvedProjectPath,
705
+ platform: options.platform,
706
+ unityArgs: options.unityArgs,
707
+ unityVersion,
708
+ };
709
+ await launch(resolved);
710
+ // Hub timestamp update is non-critical external I/O; failure should not block after successful launch
711
+ const now = new Date();
712
+ try {
713
+ await updateLastModifiedIfExists(resolvedProjectPath, now);
714
+ }
715
+ catch (error) {
716
+ const message = error instanceof Error ? error.message : String(error);
717
+ console.warn(`Failed to update Unity Hub lastModified: ${message}`);
718
+ }
719
+ const action = isRestart ? "killed-and-launched" : "launched";
720
+ return { action, projectPath: resolvedProjectPath, unityVersion };
721
+ }
659
722
  // Re-export Unity Hub functions
660
723
  export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "launch-unity",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Open a Unity project with the matching Editor version (macOS/Windows)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",