create-vidra-app 0.1.4 → 0.1.5

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.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // src/cli.ts
2
- import chalk6 from "chalk";
2
+ import chalk7 from "chalk";
3
3
 
4
4
  // src/commands/build.ts
5
5
  import path5 from "path";
6
6
  import fs4 from "fs-extra";
7
- import { execSync as execSync2 } from "child_process";
8
- import chalk4 from "chalk";
7
+ import { execSync as execSync3 } from "child_process";
8
+ import chalk5 from "chalk";
9
9
 
10
10
  // src/utils.ts
11
11
  var parseArgs = (argv) => {
@@ -22,10 +22,34 @@ var parseArgs = (argv) => {
22
22
  return args;
23
23
  };
24
24
 
25
+ // src/exec.ts
26
+ import { execSync } from "child_process";
27
+ import chalk from "chalk";
28
+ var toText = (value) => {
29
+ if (value == null) return "";
30
+ return Buffer.isBuffer(value) ? value.toString() : value;
31
+ };
32
+ var formatProcessError = (error) => {
33
+ const err = error;
34
+ const combined = [toText(err.stderr), toText(err.stdout)].map((s) => s.trim()).filter((s) => s.length > 0).join("\n").trim();
35
+ return combined.length > 0 ? combined : err.message ?? String(error);
36
+ };
37
+ var formatBuildError = (error) => {
38
+ const raw = formatProcessError(error);
39
+ const lines = raw.split(/\r?\n/);
40
+ const errorLines = lines.filter(
41
+ (line) => /(:\s*error\b|\berror\s+[A-Z]{2,}\d+|Build FAILED|MSB\d{4}|NETSDK\d{4})/i.test(
42
+ line
43
+ )
44
+ );
45
+ const picked = errorLines.length > 0 ? errorLines : lines.filter((line) => line.trim().length > 0).slice(-30);
46
+ return picked.join("\n").trim();
47
+ };
48
+
25
49
  // src/project.ts
26
50
  import path from "path";
27
51
  import fs from "fs-extra";
28
- import chalk from "chalk";
52
+ import chalk2 from "chalk";
29
53
  var detectPlatform = () => {
30
54
  switch (process.platform) {
31
55
  case "darwin":
@@ -64,7 +88,7 @@ var detectProject = (cwd) => {
64
88
  dir = parent;
65
89
  }
66
90
  console.error(
67
- chalk.red(
91
+ chalk2.red(
68
92
  " Could not detect Vidra project. Run this command from your project root.\n Expected: package.json, ui/, src/<Name>.Host/<Name>.Host.csproj"
69
93
  )
70
94
  );
@@ -97,7 +121,7 @@ var readCsprojVersion = (csprojPath) => {
97
121
  // src/targets/macos.ts
98
122
  import path2 from "path";
99
123
  import fs2 from "fs-extra";
100
- import { execSync } from "child_process";
124
+ import { execSync as execSync2 } from "child_process";
101
125
  import os from "os";
102
126
  var macosTarget = {
103
127
  name: "macos",
@@ -123,7 +147,7 @@ var macosTarget = {
123
147
  if (fs2.existsSync(dmgPath)) {
124
148
  fs2.removeSync(dmgPath);
125
149
  }
126
- execSync(
150
+ execSync2(
127
151
  `hdiutil create -volname "${volName}" -srcfolder "${staging}" -ov -format UDZO "${dmgPath}"`,
128
152
  { stdio: "pipe" }
129
153
  );
@@ -137,41 +161,35 @@ var macosTarget = {
137
161
  // src/signing.ts
138
162
  import path3 from "path";
139
163
  import { execFileSync } from "child_process";
140
- import chalk2 from "chalk";
164
+ import chalk3 from "chalk";
141
165
  var signMacAppBundleIfPossible = (appBundle, options) => {
142
166
  if (process.platform !== "darwin") return;
143
- const codesignIdentity = resolveMacCodeSigningIdentity();
144
- if (!codesignIdentity) {
145
- options.warn(
146
- chalk2.yellow(
147
- " No usable macOS signing identity found. The app will remain ad-hoc signed."
148
- )
149
- );
150
- options.warn(
151
- chalk2.yellow(
152
- " Set VIDRA_MACOS_CODESIGN_KEY to override identity selection if needed."
153
- )
154
- );
155
- return;
156
- }
167
+ const identity = resolveMacCodeSigningIdentity();
168
+ const signWith = identity ?? "-";
169
+ const label = path3.basename(appBundle);
157
170
  try {
158
171
  execFileSync(
159
172
  "codesign",
160
- ["--force", "--deep", "--sign", codesignIdentity, appBundle],
173
+ ["--force", "--deep", "--sign", signWith, appBundle],
161
174
  {
162
175
  stdio: options.verbose ? "inherit" : "pipe"
163
176
  }
164
177
  );
165
178
  options.log(
166
- ` ${chalk2.dim("Signing:")} ${chalk2.cyan(path3.basename(appBundle))} ${chalk2.dim(`with ${codesignIdentity}`)}`
179
+ identity ? ` ${chalk3.dim("Signing:")} ${chalk3.cyan(label)} ${chalk3.dim(`with ${identity}`)}` : ` ${chalk3.dim("Signing:")} ${chalk3.cyan(label)} ${chalk3.dim("ad-hoc (no developer identity)")}`
167
180
  );
168
181
  } catch (error) {
169
182
  options.warn(
170
- chalk2.yellow(
171
- " Failed to re-sign the macOS app bundle. It may remain ad-hoc signed."
183
+ chalk3.yellow(
184
+ " Could not code-sign the macOS app bundle; it may fail to launch."
185
+ )
186
+ );
187
+ options.warn(
188
+ chalk3.yellow(
189
+ " Install Xcode or the Command Line Tools (provides `codesign`), or set VIDRA_MACOS_CODESIGN_KEY."
172
190
  )
173
191
  );
174
- options.warn(chalk2.dim(formatExecError(error)));
192
+ options.warn(chalk3.dim(formatExecError(error)));
175
193
  }
176
194
  };
177
195
  var resolveMacCodeSigningIdentity = () => {
@@ -239,7 +257,7 @@ var windowsTarget = {
239
257
  // src/doctor.ts
240
258
  import { execFileSync as execFileSync2 } from "child_process";
241
259
  import prompts from "prompts";
242
- import chalk3 from "chalk";
260
+ import chalk4 from "chalk";
243
261
  var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
244
262
  var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
245
263
  var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
@@ -272,6 +290,11 @@ var looksLikeMissingWorkload = (output) => [
272
290
  /maui-windows/i,
273
291
  /to\s+install\s+the\s+.*workload/i
274
292
  ].some((re) => re.test(output));
293
+ var looksLikeMissingXcode = (output) => [
294
+ /valid\s+Xcode\s+installation\s+was\s+not\s+found/i,
295
+ /could\s+not\s+find\s+a\s+valid\s+Xcode\s+app\s+bundle/i,
296
+ /macios-missing-xcode/i
297
+ ].some((re) => re.test(output));
275
298
  var checkDotnetSdk = () => {
276
299
  const res = run(DOTNET, ["--list-sdks"]);
277
300
  if (!res.found) {
@@ -363,17 +386,17 @@ var collectRequirements = (opts = {}) => {
363
386
  };
364
387
  var printRequirements = (reqs) => {
365
388
  for (const r of reqs) {
366
- const icon = r.status === "ok" ? chalk3.green("\u2713") : r.status === "missing" ? chalk3.red("\u2717") : chalk3.yellow("?");
367
- const detail = r.detail ? ` ${chalk3.dim(`(${r.detail})`)}` : "";
389
+ const icon = r.status === "ok" ? chalk4.green("\u2713") : r.status === "missing" ? chalk4.red("\u2717") : chalk4.yellow("?");
390
+ const detail = r.detail ? ` ${chalk4.dim(`(${r.detail})`)}` : "";
368
391
  console.log(` ${icon} ${r.name}${detail}`);
369
392
  if (r.status === "missing" && r.fix) {
370
- console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(r.fix)}`);
393
+ console.log(` ${chalk4.dim("fix:")} ${chalk4.cyan(r.fix)}`);
371
394
  }
372
395
  }
373
396
  };
374
397
  var runDoctor = async () => {
375
398
  console.log();
376
- console.log(` ${chalk3.bold.cyan("vidra doctor")}`);
399
+ console.log(` ${chalk4.bold.cyan("vidra doctor")}`);
377
400
  console.log();
378
401
  const reqs = collectRequirements();
379
402
  printRequirements(reqs);
@@ -381,7 +404,7 @@ var runDoctor = async () => {
381
404
  const missing = reqs.filter((r) => r.status === "missing");
382
405
  if (missing.length === 0) {
383
406
  console.log(
384
- ` ${chalk3.green("All checks passed.")} You're ready to run ${chalk3.cyan(
407
+ ` ${chalk4.green("All checks passed.")} You're ready to run ${chalk4.cyan(
385
408
  "vidra dev"
386
409
  )}.`
387
410
  );
@@ -389,9 +412,9 @@ var runDoctor = async () => {
389
412
  return 0;
390
413
  }
391
414
  console.log(
392
- ` ${chalk3.yellow(
415
+ ` ${chalk4.yellow(
393
416
  `${missing.length} issue(s) found.`
394
- )} Apply the fixes above, then re-run ${chalk3.cyan("vidra doctor")}.`
417
+ )} Apply the fixes above, then re-run ${chalk4.cyan("vidra doctor")}.`
395
418
  );
396
419
  console.log();
397
420
  return 1;
@@ -399,9 +422,9 @@ var runDoctor = async () => {
399
422
  var installWorkload = (csprojPath) => {
400
423
  const args = csprojPath ? ["workload", "restore", csprojPath] : ["workload", "install", "maui"];
401
424
  console.log();
402
- console.log(` ${chalk3.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
425
+ console.log(` ${chalk4.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
403
426
  console.log(
404
- ` ${chalk3.dim(
427
+ ` ${chalk4.dim(
405
428
  "This can download several hundred MB and take a few minutes."
406
429
  )}`
407
430
  );
@@ -411,13 +434,13 @@ var installWorkload = (csprojPath) => {
411
434
  return true;
412
435
  } catch {
413
436
  console.error();
414
- console.error(` ${chalk3.red("Workload install failed.")}`);
437
+ console.error(` ${chalk4.red("Workload install failed.")}`);
415
438
  console.error(
416
- ` ${chalk3.dim(
439
+ ` ${chalk4.dim(
417
440
  "If this is a permissions error, your SDK is in a system location and needs elevation:"
418
441
  )}`
419
442
  );
420
- console.error(` ${chalk3.cyan("sudo dotnet workload install maui")}`);
443
+ console.error(` ${chalk4.cyan("sudo dotnet workload install maui")}`);
421
444
  console.error();
422
445
  return false;
423
446
  }
@@ -426,9 +449,9 @@ var ensureMauiWorkload = async (opts = {}) => {
426
449
  const dotnet = checkDotnetSdk();
427
450
  if (dotnet.status === "missing") {
428
451
  console.log();
429
- console.log(` ${chalk3.yellow("!")} ${dotnet.name} \u2014 ${dotnet.detail}`);
452
+ console.log(` ${chalk4.yellow("!")} ${dotnet.name} \u2014 ${dotnet.detail}`);
430
453
  if (dotnet.fix) {
431
- console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(dotnet.fix)}`);
454
+ console.log(` ${chalk4.dim("fix:")} ${chalk4.cyan(dotnet.fix)}`);
432
455
  }
433
456
  return false;
434
457
  }
@@ -436,7 +459,7 @@ var ensureMauiWorkload = async (opts = {}) => {
436
459
  if (isMauiWorkloadInstalled()) return true;
437
460
  console.log();
438
461
  console.log(
439
- ` ${chalk3.yellow("!")} The .NET MAUI workload is required but not installed.`
462
+ ` ${chalk4.yellow("!")} The .NET MAUI workload is required but not installed.`
440
463
  );
441
464
  const interactive = opts.interactive ?? isInteractive();
442
465
  if (interactive) {
@@ -454,31 +477,50 @@ var ensureMauiWorkload = async (opts = {}) => {
454
477
  }
455
478
  if (install) {
456
479
  if (installWorkload(opts.csprojPath) && isMauiWorkloadInstalled()) {
457
- console.log(` ${chalk3.green("\u2713")} MAUI workload installed.`);
480
+ console.log(` ${chalk4.green("\u2713")} MAUI workload installed.`);
458
481
  return true;
459
482
  }
460
483
  return false;
461
484
  }
462
485
  }
463
486
  console.log(
464
- ` ${chalk3.dim("run:")} ${chalk3.cyan("dotnet workload install maui")}`
487
+ ` ${chalk4.dim("run:")} ${chalk4.cyan("dotnet workload install maui")}`
465
488
  );
466
- console.log(` ${chalk3.dim("docs:")} ${chalk3.cyan(MAUI_DOCS)}`);
489
+ console.log(` ${chalk4.dim("docs:")} ${chalk4.cyan(MAUI_DOCS)}`);
467
490
  return false;
468
491
  };
469
492
  var printWorkloadHint = () => {
470
493
  console.error();
471
494
  console.error(
472
- ` ${chalk3.yellow("This looks like a missing .NET MAUI workload.")}`
495
+ ` ${chalk4.yellow("This looks like a missing .NET MAUI workload.")}`
473
496
  );
474
497
  console.error(
475
- ` ${chalk3.dim("fix: ")} ${chalk3.cyan("dotnet workload install maui")}`
498
+ ` ${chalk4.dim("fix: ")} ${chalk4.cyan("dotnet workload install maui")}`
476
499
  );
477
500
  console.error(
478
- ` ${chalk3.dim("check:")} ${chalk3.cyan("vidra doctor")}`
501
+ ` ${chalk4.dim("check:")} ${chalk4.cyan("vidra doctor")}`
479
502
  );
480
503
  console.error();
481
504
  };
505
+ var printXcodeHint = () => {
506
+ console.error();
507
+ console.error(
508
+ ` ${chalk4.yellow(
509
+ "Mac Catalyst builds need the full Xcode app, not just the Command Line Tools."
510
+ )}`
511
+ );
512
+ console.error(` ${chalk4.dim("1.")} Install Xcode from the App Store`);
513
+ console.error(
514
+ ` ${chalk4.dim("2.")} ${chalk4.cyan(
515
+ "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"
516
+ )}`
517
+ );
518
+ console.error(
519
+ ` ${chalk4.dim("3.")} ${chalk4.cyan("sudo xcodebuild -runFirstLaunch")}`
520
+ );
521
+ console.error(` ${chalk4.dim("check:")} ${chalk4.cyan("vidra doctor")}`);
522
+ console.error();
523
+ };
482
524
 
483
525
  // src/commands/build.ts
484
526
  var VERSION = "0.1.0";
@@ -492,22 +534,22 @@ var buildCommand = async (argv) => {
492
534
  const targetName = args["target"] || detectPlatform();
493
535
  console.log();
494
536
  console.log(
495
- ` ${chalk4.bold.cyan("vidra build")} ${chalk4.dim(`v${VERSION}`)}`
537
+ ` ${chalk5.bold.cyan("vidra build")} ${chalk5.dim(`v${VERSION}`)}`
496
538
  );
497
539
  console.log();
498
540
  const target = TARGETS[targetName];
499
541
  if (!target) {
500
542
  const supported = Object.keys(TARGETS).join(", ");
501
543
  console.error(
502
- chalk4.red(
544
+ chalk5.red(
503
545
  ` Unsupported target: ${targetName}. Supported: ${supported}`
504
546
  )
505
547
  );
506
548
  process.exit(1);
507
549
  }
508
550
  const project = detectProject(process.cwd());
509
- console.log(` ${chalk4.dim("Project:")} ${chalk4.cyan(project.projectName)}`);
510
- console.log(` ${chalk4.dim("Target:")} ${chalk4.cyan(target.name)} (${target.framework})`);
551
+ console.log(` ${chalk5.dim("Project:")} ${chalk5.cyan(project.projectName)}`);
552
+ console.log(` ${chalk5.dim("Target:")} ${chalk5.cyan(target.name)} (${target.framework})`);
511
553
  console.log();
512
554
  if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
513
555
  process.exit(1);
@@ -518,12 +560,12 @@ var buildCommand = async (argv) => {
518
560
  const bundlePath = target.findBundle(publishDir, project.projectName);
519
561
  if (!bundlePath) {
520
562
  console.error(
521
- chalk4.red(` Could not find build artifact in ${publishDir}`)
563
+ chalk5.red(` Could not find build artifact in ${publishDir}`)
522
564
  );
523
565
  process.exit(1);
524
566
  }
525
567
  console.log(
526
- ` ${chalk4.dim("Bundle:")} ${chalk4.cyan(path5.basename(bundlePath))}`
568
+ ` ${chalk5.dim("Bundle:")} ${chalk5.cyan(path5.basename(bundlePath))}`
527
569
  );
528
570
  if (target.name === "macos") {
529
571
  signMacAppBundleIfPossible(bundlePath, {
@@ -535,7 +577,7 @@ var buildCommand = async (argv) => {
535
577
  const outputDir = path5.join(project.root, "dist");
536
578
  fs4.ensureDirSync(outputDir);
537
579
  console.log();
538
- console.log(` ${chalk4.dim(`Packaging for ${target.name}...`)}`);
580
+ console.log(` ${chalk5.dim(`Packaging for ${target.name}...`)}`);
539
581
  const startPkg = Date.now();
540
582
  let outputPath;
541
583
  try {
@@ -544,46 +586,44 @@ var buildCommand = async (argv) => {
544
586
  displayVersion: project.displayVersion
545
587
  });
546
588
  } catch (e) {
547
- const err = e;
548
- console.error(chalk4.red(` Packaging failed.`));
549
- console.error(chalk4.dim(err.stderr?.toString() || err.message));
589
+ console.error(chalk5.red(` Packaging failed.`));
590
+ console.error(chalk5.dim(formatProcessError(e)));
550
591
  process.exit(1);
551
592
  }
552
593
  const pkgTime = ((Date.now() - startPkg) / 1e3).toFixed(1);
553
594
  const sizeBytes = fs4.statSync(outputPath).size;
554
595
  const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(1);
555
596
  console.log(
556
- ` ${chalk4.green(">")} ${path5.basename(outputPath)} ${chalk4.dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
597
+ ` ${chalk5.green(">")} ${path5.basename(outputPath)} ${chalk5.dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
557
598
  );
558
599
  console.log();
559
600
  console.log(
560
- ` ${chalk4.green("Done!")} Output: ${chalk4.cyan(path5.relative(project.root, outputPath))}`
601
+ ` ${chalk5.green("Done!")} Output: ${chalk5.cyan(path5.relative(project.root, outputPath))}`
561
602
  );
562
603
  console.log();
563
604
  };
564
605
  var stepBuildUi = (project, verbose) => {
565
- console.log(` ${chalk4.dim("Building UI...")}`);
606
+ console.log(` ${chalk5.dim("Building UI...")}`);
566
607
  const start = Date.now();
567
608
  try {
568
- execSync2("npm run build", {
609
+ execSync3("npm run build", {
569
610
  cwd: project.uiDir,
570
611
  stdio: verbose ? "inherit" : "pipe"
571
612
  });
572
613
  } catch (e) {
573
- const err = e;
574
- console.error(chalk4.red(" Vite build failed."));
575
- console.error(chalk4.dim(err.stderr?.toString() || err.message));
614
+ console.error(chalk5.red(" Vite build failed."));
615
+ console.error(chalk5.dim(formatBuildError(e)));
576
616
  process.exit(1);
577
617
  }
578
618
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
579
- console.log(` ${chalk4.green(">")} Vite build complete ${chalk4.dim(`(${elapsed}s)`)}`);
619
+ console.log(` ${chalk5.green(">")} Vite build complete ${chalk5.dim(`(${elapsed}s)`)}`);
580
620
  console.log();
581
621
  };
582
622
  var stepCopyAssets = (project) => {
583
- console.log(` ${chalk4.dim("Copying assets to host project...")}`);
623
+ console.log(` ${chalk5.dim("Copying assets to host project...")}`);
584
624
  const viteDist = path5.join(project.uiDir, "dist");
585
625
  if (!fs4.existsSync(viteDist)) {
586
- console.error(chalk4.red(` ui/dist not found. Vite build may have failed.`));
626
+ console.error(chalk5.red(` ui/dist not found. Vite build may have failed.`));
587
627
  process.exit(1);
588
628
  }
589
629
  const wwwroot = path5.join(project.hostDir, "Resources", "Raw", "wwwroot");
@@ -591,7 +631,7 @@ var stepCopyAssets = (project) => {
591
631
  fs4.copySync(viteDist, wwwroot);
592
632
  const fileCount = countFiles(wwwroot);
593
633
  console.log(
594
- ` ${chalk4.green(">")} ${fileCount} files -> ${chalk4.dim("Resources/Raw/wwwroot/")}`
634
+ ` ${chalk5.green(">")} ${fileCount} files -> ${chalk5.dim("Resources/Raw/wwwroot/")}`
595
635
  );
596
636
  console.log();
597
637
  };
@@ -608,12 +648,12 @@ var countFiles = (dir) => {
608
648
  };
609
649
  var stepDotnetPublish = (project, target, verbose) => {
610
650
  console.log(
611
- ` ${chalk4.dim(`Publishing .NET host (${target.framework})...`)}`
651
+ ` ${chalk5.dim(`Publishing .NET host (${target.framework})...`)}`
612
652
  );
613
653
  const start = Date.now();
614
654
  const extraArgs = target.extraPublishArgs ?? "-p:CreatePackage=false";
615
655
  try {
616
- execSync2(
656
+ execSync3(
617
657
  `dotnet publish "${project.csprojPath}" -c Release -f ${target.framework} ${extraArgs}`,
618
658
  {
619
659
  cwd: project.root,
@@ -621,16 +661,21 @@ var stepDotnetPublish = (project, target, verbose) => {
621
661
  }
622
662
  );
623
663
  } catch (e) {
624
- const err = e;
625
- const output = err.stderr?.toString() || err.message;
626
- console.error(chalk4.red(" dotnet publish failed."));
627
- console.error(chalk4.dim(output));
664
+ const output = formatBuildError(e);
665
+ console.error(chalk5.red(" dotnet publish failed."));
666
+ console.error(chalk5.dim(output));
628
667
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
668
+ else if (looksLikeMissingXcode(output)) printXcodeHint();
669
+ if (!verbose) {
670
+ console.error(
671
+ chalk5.dim(" Re-run with --verbose for the full build log.")
672
+ );
673
+ }
629
674
  process.exit(1);
630
675
  }
631
676
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
632
677
  console.log(
633
- ` ${chalk4.green(">")} dotnet publish complete ${chalk4.dim(`(${elapsed}s)`)}`
678
+ ` ${chalk5.green(">")} dotnet publish complete ${chalk5.dim(`(${elapsed}s)`)}`
634
679
  );
635
680
  const publishDir = path5.join(
636
681
  project.hostDir,
@@ -646,7 +691,7 @@ import path6 from "path";
646
691
  import fs5 from "fs-extra";
647
692
  import { execFileSync as execFileSync3, spawn } from "child_process";
648
693
  import { request } from "http";
649
- import chalk5 from "chalk";
694
+ import chalk6 from "chalk";
650
695
  var VERSION2 = "0.1.0";
651
696
  var POLL_INTERVAL_MS = 500;
652
697
  var POLL_TIMEOUT_MS = 3e4;
@@ -662,7 +707,9 @@ var TARGETS2 = {
662
707
  framework: "net10.0-windows10.0.19041.0"
663
708
  }
664
709
  };
665
- var devCommand = async (argv) => {
710
+ var devCommand = (argv) => startSession(argv, { vite: true });
711
+ var runCommand = (argv) => startSession(argv, { vite: false });
712
+ var startSession = async (argv, opts) => {
666
713
  const args = parseArgs(["_", "_", ...argv]);
667
714
  const targetName = args["target"] || detectPlatform();
668
715
  const verbose = !!args["verbose"];
@@ -671,7 +718,7 @@ var devCommand = async (argv) => {
671
718
  if (!target) {
672
719
  const supported = Object.keys(TARGETS2).join(", ");
673
720
  console.error(
674
- chalk5.red(
721
+ chalk6.red(
675
722
  ` Unsupported target: ${targetName}. Supported: ${supported}`
676
723
  )
677
724
  );
@@ -682,15 +729,16 @@ var devCommand = async (argv) => {
682
729
  if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
683
730
  process.exit(1);
684
731
  }
685
- const session = new DevSession(project, target, viteUrl, verbose);
732
+ const session = new DevSession(project, target, viteUrl, verbose, opts);
686
733
  await session.run();
687
734
  };
688
735
  var DevSession = class {
689
- constructor(project, target, viteUrl, verbose) {
736
+ constructor(project, target, viteUrl, verbose, options = {}) {
690
737
  this.project = project;
691
738
  this.target = target;
692
739
  this.viteUrl = viteUrl;
693
740
  this.verbose = verbose;
741
+ this.vite = options.vite ?? true;
694
742
  }
695
743
  project;
696
744
  target;
@@ -698,32 +746,39 @@ var DevSession = class {
698
746
  verbose;
699
747
  children = [];
700
748
  buildConfig = process.env.VIDRA_BUILD_CONFIG || "Debug";
749
+ vite;
701
750
  shuttingDown = false;
702
751
  async run() {
703
752
  this.installSignalHandlers();
704
753
  console.log();
705
- console.log(` ${chalk5.bold.cyan("vidra dev")} ${chalk5.dim(`v${VERSION2}`)}`);
754
+ console.log(
755
+ ` ${chalk6.bold.cyan(this.vite ? "vidra dev" : "vidra run")} ${chalk6.dim(`v${VERSION2}`)}`
756
+ );
706
757
  console.log();
707
- console.log(` ${chalk5.dim("Project:")} ${chalk5.cyan(this.project.projectName)}`);
758
+ console.log(` ${chalk6.dim("Project:")} ${chalk6.cyan(this.project.projectName)}`);
708
759
  console.log(
709
- ` ${chalk5.dim("Target:")} ${chalk5.cyan(this.target.name)} (${this.target.framework})`
760
+ ` ${chalk6.dim("Target:")} ${chalk6.cyan(this.target.name)} (${this.target.framework})`
710
761
  );
711
762
  console.log();
712
- const vite = this.startVite();
713
- try {
714
- await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
715
- } catch (error) {
716
- console.error(
717
- chalk5.red(
718
- ` ${error.message}`
719
- )
763
+ let vite;
764
+ if (this.vite) {
765
+ vite = this.startVite();
766
+ try {
767
+ await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
768
+ } catch (error) {
769
+ console.error(chalk6.red(` ${error.message}`));
770
+ this.shutdown(1);
771
+ }
772
+ console.log(` ${chalk6.dim("Vite:")} ${chalk6.cyan(this.viteUrl)}`);
773
+ console.log();
774
+ } else {
775
+ console.log(
776
+ ` ${chalk6.dim("UI:")} ${chalk6.cyan(this.viteUrl)} ${chalk6.dim("(start it separately, e.g. `npm run dev:ui`)")}`
720
777
  );
721
- this.shutdown(1);
778
+ console.log();
722
779
  }
723
- console.log(` ${chalk5.dim("Vite:")} ${chalk5.cyan(this.viteUrl)}`);
724
- console.log();
725
780
  const host = this.target.name === "macos" ? this.launchMacosHost() : this.launchWindowsHost();
726
- await waitForExit(vite, host);
781
+ await waitForExit(...vite ? [vite, host] : [host]);
727
782
  }
728
783
  installSignalHandlers() {
729
784
  process.on("SIGINT", () => {
@@ -735,7 +790,7 @@ var DevSession = class {
735
790
  });
736
791
  }
737
792
  startVite() {
738
- console.log(` ${chalk5.dim("Starting Vite dev server...")}`);
793
+ console.log(` ${chalk6.dim("Starting Vite dev server...")}`);
739
794
  const vite = spawn(NPM_COMMAND, ["run", "dev"], {
740
795
  cwd: this.project.uiDir,
741
796
  stdio: ["ignore", "pipe", "pipe"]
@@ -744,7 +799,7 @@ var DevSession = class {
744
799
  }
745
800
  launchMacosHost() {
746
801
  console.log(
747
- ` ${chalk5.dim(`Building MAUI host (${this.target.framework})...`)}`
802
+ ` ${chalk6.dim(`Building MAUI host (${this.target.framework})...`)}`
748
803
  );
749
804
  try {
750
805
  execFileSync3(
@@ -763,10 +818,16 @@ var DevSession = class {
763
818
  }
764
819
  );
765
820
  } catch (error) {
766
- const output = formatExecError2(error);
767
- console.error(chalk5.red(" MAUI build failed."));
768
- console.error(chalk5.dim(output));
821
+ const output = formatBuildError(error);
822
+ console.error(chalk6.red(" MAUI build failed."));
823
+ console.error(chalk6.dim(output));
769
824
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
825
+ else if (looksLikeMissingXcode(output)) printXcodeHint();
826
+ if (!this.verbose) {
827
+ console.error(
828
+ chalk6.dim(" Re-run with --verbose for the full build log.")
829
+ );
830
+ }
770
831
  process.exit(1);
771
832
  }
772
833
  const appBundle = findMacAppBundle(
@@ -776,7 +837,7 @@ var DevSession = class {
776
837
  );
777
838
  if (!appBundle) {
778
839
  console.error(
779
- chalk5.red(
840
+ chalk6.red(
780
841
  ` Could not find .app bundle in ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
781
842
  )
782
843
  );
@@ -790,11 +851,11 @@ var DevSession = class {
790
851
  const binary = findMacExecutable(appBundle);
791
852
  if (!binary) {
792
853
  console.error(
793
- chalk5.red(` Could not find the app executable in ${appBundle}.`)
854
+ chalk6.red(` Could not find the app executable in ${appBundle}.`)
794
855
  );
795
856
  process.exit(1);
796
857
  }
797
- console.log(` ${chalk5.dim("Launching host...")}`);
858
+ console.log(` ${chalk6.dim("Launching host...")}`);
798
859
  const host = spawn(binary, [], {
799
860
  cwd: this.project.root,
800
861
  stdio: ["ignore", "pipe", "pipe"],
@@ -803,7 +864,7 @@ var DevSession = class {
803
864
  return this.registerChild(host, "host", path6.basename(binary));
804
865
  }
805
866
  launchWindowsHost() {
806
- console.log(` ${chalk5.dim("Launching host...")}`);
867
+ console.log(` ${chalk6.dim("Launching host...")}`);
807
868
  const host = spawn(
808
869
  DOTNET_COMMAND,
809
870
  [
@@ -827,27 +888,38 @@ var DevSession = class {
827
888
  this.children.push(child);
828
889
  prefixStream(child.stdout, tag);
829
890
  prefixStream(child.stderr, tag);
830
- child.on("exit", (code) => {
891
+ child.on("exit", (code, signal) => {
831
892
  if (this.shuttingDown) return;
832
893
  if (tag === "ui") {
833
894
  const exitCode = code ?? 1;
834
895
  console.error(
835
- chalk5.red(`
896
+ chalk6.red(`
836
897
  ${label} exited with code ${exitCode}.`)
837
898
  );
838
899
  this.shutdown(exitCode);
839
900
  return;
840
901
  }
841
- if (code !== null && code !== 0) {
842
- console.error(chalk5.red(`
843
- ${label} exited with code ${code}.`));
902
+ const failed = code !== null && code !== 0 || signal !== null;
903
+ if (failed) {
904
+ console.error(
905
+ chalk6.red(
906
+ `
907
+ ${label} exited with ${signal ? `signal ${signal}` : `code ${code}`}.`
908
+ )
909
+ );
910
+ if (tag === "host" && this.target.name === "macos") {
911
+ printMacLaunchHint();
912
+ }
844
913
  }
845
- this.shutdown(code ?? 0);
914
+ this.shutdown(code ?? (signal ? 1 : 0));
846
915
  });
847
916
  child.on("error", (error) => {
848
917
  if (this.shuttingDown) return;
849
- console.error(chalk5.red(`
918
+ console.error(chalk6.red(`
850
919
  Failed to start ${label}: ${error.message}`));
920
+ if (tag === "host" && this.target.name === "macos") {
921
+ printMacLaunchHint();
922
+ }
851
923
  this.shutdown(1);
852
924
  });
853
925
  return child;
@@ -863,12 +935,12 @@ var DevSession = class {
863
935
  };
864
936
  var ensureTargetMatchesHostOs = (targetName) => {
865
937
  if (targetName === "macos" && process.platform !== "darwin") {
866
- console.error(chalk5.red(" The macOS dev target can only run on macOS."));
938
+ console.error(chalk6.red(" The macOS dev target can only run on macOS."));
867
939
  process.exit(1);
868
940
  }
869
941
  if (targetName === "windows" && process.platform !== "win32") {
870
942
  console.error(
871
- chalk5.red(" The Windows dev target can only run on Windows.")
943
+ chalk6.red(" The Windows dev target can only run on Windows.")
872
944
  );
873
945
  process.exit(1);
874
946
  }
@@ -916,11 +988,12 @@ var waitForServer = (url, timeoutMs) => {
916
988
  poll();
917
989
  });
918
990
  };
919
- var waitForExit = (vite, host) => {
991
+ var waitForExit = (...children) => {
920
992
  return new Promise((resolve) => {
921
993
  const resolveOnce = () => resolve();
922
- vite.once("exit", resolveOnce);
923
- host.once("exit", resolveOnce);
994
+ for (const child of children) {
995
+ child.once("exit", resolveOnce);
996
+ }
924
997
  });
925
998
  };
926
999
  var findMacAppBundle = (hostDir, framework, buildConfig) => {
@@ -965,39 +1038,50 @@ var killChild = (child) => {
965
1038
  }
966
1039
  child.kill("SIGTERM");
967
1040
  };
968
- var formatExecError2 = (error) => {
969
- const err = error;
970
- if (Buffer.isBuffer(err.stderr)) {
971
- return err.stderr.toString();
972
- }
973
- if (typeof err.stderr === "string") {
974
- return err.stderr;
975
- }
976
- return err.message;
1041
+ var printMacLaunchHint = () => {
1042
+ console.error();
1043
+ console.error(chalk6.yellow(" The host built but the app couldn't launch."));
1044
+ console.error(
1045
+ chalk6.dim(
1046
+ " On macOS this is usually code signing / Gatekeeper for a locally built app:"
1047
+ )
1048
+ );
1049
+ console.error(
1050
+ ` ${chalk6.dim("\u2022")} Install full Xcode, then run ${chalk6.cyan("vidra doctor")} to verify`
1051
+ );
1052
+ console.error(
1053
+ ` ${chalk6.dim("\u2022")} Approve it once in Finder: right-click the ${chalk6.cyan(".app")} and choose ${chalk6.cyan("Open")}`
1054
+ );
1055
+ console.error(
1056
+ ` ${chalk6.dim("\u2022")} Or provide a signing identity via ${chalk6.cyan("VIDRA_MACOS_CODESIGN_KEY")}`
1057
+ );
1058
+ console.error();
977
1059
  };
978
1060
 
979
1061
  // src/cli.ts
980
- var VERSION3 = "0.1.3";
1062
+ var VERSION3 = "0.1.5";
981
1063
  var printHelp = () => {
982
1064
  console.log(`
983
- ${chalk6.bold("vidra")} ${chalk6.dim(`v${VERSION3}`)}
1065
+ ${chalk7.bold("vidra")} ${chalk7.dim(`v${VERSION3}`)}
984
1066
 
985
- ${chalk6.dim("Usage:")}
1067
+ ${chalk7.dim("Usage:")}
986
1068
  vidra <command> [options]
987
1069
 
988
- ${chalk6.dim("Commands:")}
989
- dev Start the development environment
1070
+ ${chalk7.dim("Commands:")}
1071
+ dev Start the development environment (Vite + native host)
1072
+ run Build and launch the native host only (no Vite dev server)
990
1073
  build Build and package the application for distribution
991
1074
  doctor Check that your environment is set up to build Vidra apps
992
1075
  help Show this help message
993
1076
 
994
- ${chalk6.dim("Examples:")}
995
- vidra dev ${chalk6.dim("# start Vite + native host")}
996
- vidra dev --target windows ${chalk6.dim("# run the Windows host")}
997
- vidra build ${chalk6.dim("# auto-detect platform")}
998
- vidra build --target macos ${chalk6.dim("# macOS DMG")}
999
- vidra build --verbose ${chalk6.dim("# show full build output")}
1000
- vidra doctor ${chalk6.dim("# verify .NET SDK + MAUI workload")}
1077
+ ${chalk7.dim("Examples:")}
1078
+ vidra dev ${chalk7.dim("# start Vite + native host")}
1079
+ vidra dev --target windows ${chalk7.dim("# run the Windows host")}
1080
+ vidra run ${chalk7.dim("# launch the host (UI served separately)")}
1081
+ vidra build ${chalk7.dim("# auto-detect platform")}
1082
+ vidra build --target macos ${chalk7.dim("# macOS DMG")}
1083
+ vidra build --verbose ${chalk7.dim("# show full build output")}
1084
+ vidra doctor ${chalk7.dim("# verify .NET SDK + MAUI workload")}
1001
1085
  `);
1002
1086
  };
1003
1087
  var main = async () => {
@@ -1007,6 +1091,9 @@ var main = async () => {
1007
1091
  case "dev":
1008
1092
  await devCommand(args.slice(1));
1009
1093
  break;
1094
+ case "run":
1095
+ await runCommand(args.slice(1));
1096
+ break;
1010
1097
  case "build":
1011
1098
  await buildCommand(args.slice(1));
1012
1099
  break;
@@ -1024,13 +1111,13 @@ var main = async () => {
1024
1111
  console.log(VERSION3);
1025
1112
  break;
1026
1113
  default:
1027
- console.error(chalk6.red(` Unknown command: ${command}
1114
+ console.error(chalk7.red(` Unknown command: ${command}
1028
1115
  `));
1029
1116
  printHelp();
1030
1117
  process.exit(1);
1031
1118
  }
1032
1119
  };
1033
1120
  main().catch((e) => {
1034
- console.error(chalk6.red(e.message));
1121
+ console.error(chalk7.red(e.message));
1035
1122
  process.exit(1);
1036
1123
  });
package/dist/index.js CHANGED
@@ -37,13 +37,21 @@ var parseArgs = (argv) => {
37
37
  // src/exec.ts
38
38
  import { execSync } from "child_process";
39
39
  import chalk from "chalk";
40
+ var toText = (value) => {
41
+ if (value == null) return "";
42
+ return Buffer.isBuffer(value) ? value.toString() : value;
43
+ };
44
+ var formatProcessError = (error) => {
45
+ const err = error;
46
+ const combined = [toText(err.stderr), toText(err.stdout)].map((s) => s.trim()).filter((s) => s.length > 0).join("\n").trim();
47
+ return combined.length > 0 ? combined : err.message ?? String(error);
48
+ };
40
49
  var exec = (cmd, cwd) => {
41
50
  try {
42
51
  execSync(cmd, { cwd, stdio: "pipe" });
43
52
  } catch (e) {
44
- const err = e;
45
53
  console.error(chalk.red(` Command failed: ${cmd}`));
46
- console.error(chalk.dim(err.stderr?.toString() || err.message));
54
+ console.error(chalk.dim(formatProcessError(e)));
47
55
  process.exit(1);
48
56
  }
49
57
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vidra-app",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Scaffold a new Vidra application (React + .NET MAUI)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,8 +5,8 @@
5
5
  "build": "vidra build",
6
6
  "dev": "vidra dev",
7
7
  "dev:ui": "npm run dev --prefix ui",
8
- "dev:host:macos": "dotnet build -t:Run -f net10.0-maccatalyst src/{{projectName}}.Host/{{projectName}}.Host.csproj",
9
- "dev:host:windows": "dotnet build -t:Run -f net10.0-windows10.0.19041.0 src/{{projectName}}.Host/{{projectName}}.Host.csproj"
8
+ "dev:host:macos": "vidra run --target macos",
9
+ "dev:host:windows": "vidra run --target windows"
10
10
  },
11
11
  "devDependencies": {
12
12
  "create-vidra-app": "{{cliVersion}}"