create-vidra-app 0.1.2 → 0.1.4
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/README.md +8 -2
- package/dist/cli.js +318 -59
- package/dist/index.js +169 -21
- package/package.json +1 -1
- package/templates/react-vite/README.md +12 -2
- package/templates/react-vite/ui/src/App.tsx +78 -49
- package/templates/react-vite/ui/src/index.css +280 -45
package/README.md
CHANGED
|
@@ -26,13 +26,19 @@ This package also installs the `vidra` CLI used by scaffolded apps:
|
|
|
26
26
|
vidra dev # start Vite + native host
|
|
27
27
|
vidra dev --target windows # run the Windows host
|
|
28
28
|
vidra build --target macos # build + package a macOS .dmg
|
|
29
|
+
vidra doctor # check your .NET / MAUI / Xcode setup
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
## Prerequisites
|
|
32
33
|
|
|
33
|
-
- .NET 10 SDK
|
|
34
|
+
- .NET 10 SDK
|
|
35
|
+
- The .NET MAUI workload: `dotnet workload install maui`
|
|
34
36
|
- Node.js 18+
|
|
35
|
-
- Windows targets must be built on Windows
|
|
37
|
+
- macOS targets require Xcode; Windows targets must be built on Windows
|
|
38
|
+
|
|
39
|
+
If the MAUI workload is missing, `create-vidra-app` will detect it after
|
|
40
|
+
scaffolding and offer to install it for you. You can re-check at any time with
|
|
41
|
+
`vidra doctor`.
|
|
36
42
|
|
|
37
43
|
## License
|
|
38
44
|
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/cli.ts
|
|
2
|
-
import
|
|
2
|
+
import chalk6 from "chalk";
|
|
3
3
|
|
|
4
4
|
// src/commands/build.ts
|
|
5
5
|
import path5 from "path";
|
|
6
6
|
import fs4 from "fs-extra";
|
|
7
7
|
import { execSync as execSync2 } from "child_process";
|
|
8
|
-
import
|
|
8
|
+
import chalk4 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/utils.ts
|
|
11
11
|
var parseArgs = (argv) => {
|
|
@@ -236,6 +236,250 @@ var windowsTarget = {
|
|
|
236
236
|
}
|
|
237
237
|
};
|
|
238
238
|
|
|
239
|
+
// src/doctor.ts
|
|
240
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
241
|
+
import prompts from "prompts";
|
|
242
|
+
import chalk3 from "chalk";
|
|
243
|
+
var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
|
|
244
|
+
var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
|
|
245
|
+
var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
|
|
246
|
+
var run = (cmd, args) => {
|
|
247
|
+
try {
|
|
248
|
+
const stdout = execFileSync2(cmd, args, {
|
|
249
|
+
encoding: "utf-8",
|
|
250
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
251
|
+
});
|
|
252
|
+
return { found: true, ok: true, stdout: stdout ?? "", stderr: "" };
|
|
253
|
+
} catch (e) {
|
|
254
|
+
const err = e;
|
|
255
|
+
return {
|
|
256
|
+
found: err.code !== "ENOENT",
|
|
257
|
+
ok: false,
|
|
258
|
+
stdout: bufToStr(err.stdout),
|
|
259
|
+
stderr: bufToStr(err.stderr)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var hasNet10Sdk = (listSdksOutput) => listSdksOutput.split(/\r?\n/).some((line) => /^10\./.test(line.trim()));
|
|
264
|
+
var newestNet10Sdk = (listSdksOutput) => listSdksOutput.split(/\r?\n/).map((line) => line.trim().split(/\s+/)[0]).filter((v) => /^10\./.test(v)).sort(
|
|
265
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
|
|
266
|
+
).pop();
|
|
267
|
+
var outputMentionsMaui = (workloadListOutput) => /\bmaui\b/i.test(workloadListOutput);
|
|
268
|
+
var looksLikeMissingWorkload = (output) => [
|
|
269
|
+
/NETSDK1147/i,
|
|
270
|
+
/workloads?\s+must\s+be\s+installed/i,
|
|
271
|
+
/maui-maccatalyst/i,
|
|
272
|
+
/maui-windows/i,
|
|
273
|
+
/to\s+install\s+the\s+.*workload/i
|
|
274
|
+
].some((re) => re.test(output));
|
|
275
|
+
var checkDotnetSdk = () => {
|
|
276
|
+
const res = run(DOTNET, ["--list-sdks"]);
|
|
277
|
+
if (!res.found) {
|
|
278
|
+
return {
|
|
279
|
+
name: ".NET SDK",
|
|
280
|
+
status: "missing",
|
|
281
|
+
detail: "`dotnet` was not found on your PATH",
|
|
282
|
+
fix: "Install the .NET 10 SDK \u2014 https://dotnet.microsoft.com/download"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (!res.ok && !res.stdout) {
|
|
286
|
+
return {
|
|
287
|
+
name: ".NET SDK",
|
|
288
|
+
status: "unknown",
|
|
289
|
+
detail: "could not run `dotnet --list-sdks`"
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if (hasNet10Sdk(res.stdout)) {
|
|
293
|
+
const newest = newestNet10Sdk(res.stdout);
|
|
294
|
+
return {
|
|
295
|
+
name: ".NET SDK",
|
|
296
|
+
status: "ok",
|
|
297
|
+
detail: newest ? `found ${newest}` : "found 10.x"
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
name: ".NET SDK",
|
|
302
|
+
status: "missing",
|
|
303
|
+
detail: "no 10.x SDK installed",
|
|
304
|
+
fix: "Install the .NET 10 SDK \u2014 https://dotnet.microsoft.com/download"
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
var checkMauiWorkload = (dotnetOk) => {
|
|
308
|
+
if (!dotnetOk) {
|
|
309
|
+
return {
|
|
310
|
+
name: ".NET MAUI workload",
|
|
311
|
+
status: "unknown",
|
|
312
|
+
detail: "requires the .NET SDK first"
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const res = run(DOTNET, ["workload", "list"]);
|
|
316
|
+
if (!res.found) {
|
|
317
|
+
return {
|
|
318
|
+
name: ".NET MAUI workload",
|
|
319
|
+
status: "unknown",
|
|
320
|
+
detail: "could not query workloads"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (outputMentionsMaui(res.stdout)) {
|
|
324
|
+
return { name: ".NET MAUI workload", status: "ok", detail: "installed" };
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
name: ".NET MAUI workload",
|
|
328
|
+
status: "missing",
|
|
329
|
+
detail: "not installed",
|
|
330
|
+
fix: "dotnet workload install maui"
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
var checkXcode = () => {
|
|
334
|
+
const res = run("xcode-select", ["-p"]);
|
|
335
|
+
if (!res.found || !res.ok) {
|
|
336
|
+
return {
|
|
337
|
+
name: "Xcode",
|
|
338
|
+
status: "missing",
|
|
339
|
+
detail: "not found",
|
|
340
|
+
fix: "Install Xcode from the App Store"
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const devDir = res.stdout.trim();
|
|
344
|
+
if (/CommandLineTools/i.test(devDir)) {
|
|
345
|
+
return {
|
|
346
|
+
name: "Xcode",
|
|
347
|
+
status: "missing",
|
|
348
|
+
detail: "only Command Line Tools detected (Mac Catalyst needs full Xcode)",
|
|
349
|
+
fix: "Install Xcode, then: sudo xcode-select -s /Applications/Xcode.app"
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return { name: "Xcode", status: "ok", detail: devDir };
|
|
353
|
+
};
|
|
354
|
+
var isMauiWorkloadInstalled = () => outputMentionsMaui(run(DOTNET, ["workload", "list"]).stdout);
|
|
355
|
+
var isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
356
|
+
var collectRequirements = (opts = {}) => {
|
|
357
|
+
const dotnet = checkDotnetSdk();
|
|
358
|
+
const reqs = [dotnet, checkMauiWorkload(dotnet.status === "ok")];
|
|
359
|
+
if (opts.includeXcode ?? process.platform === "darwin") {
|
|
360
|
+
reqs.push(checkXcode());
|
|
361
|
+
}
|
|
362
|
+
return reqs;
|
|
363
|
+
};
|
|
364
|
+
var printRequirements = (reqs) => {
|
|
365
|
+
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})`)}` : "";
|
|
368
|
+
console.log(` ${icon} ${r.name}${detail}`);
|
|
369
|
+
if (r.status === "missing" && r.fix) {
|
|
370
|
+
console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(r.fix)}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
var runDoctor = async () => {
|
|
375
|
+
console.log();
|
|
376
|
+
console.log(` ${chalk3.bold.cyan("vidra doctor")}`);
|
|
377
|
+
console.log();
|
|
378
|
+
const reqs = collectRequirements();
|
|
379
|
+
printRequirements(reqs);
|
|
380
|
+
console.log();
|
|
381
|
+
const missing = reqs.filter((r) => r.status === "missing");
|
|
382
|
+
if (missing.length === 0) {
|
|
383
|
+
console.log(
|
|
384
|
+
` ${chalk3.green("All checks passed.")} You're ready to run ${chalk3.cyan(
|
|
385
|
+
"vidra dev"
|
|
386
|
+
)}.`
|
|
387
|
+
);
|
|
388
|
+
console.log();
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
console.log(
|
|
392
|
+
` ${chalk3.yellow(
|
|
393
|
+
`${missing.length} issue(s) found.`
|
|
394
|
+
)} Apply the fixes above, then re-run ${chalk3.cyan("vidra doctor")}.`
|
|
395
|
+
);
|
|
396
|
+
console.log();
|
|
397
|
+
return 1;
|
|
398
|
+
};
|
|
399
|
+
var installWorkload = (csprojPath) => {
|
|
400
|
+
const args = csprojPath ? ["workload", "restore", csprojPath] : ["workload", "install", "maui"];
|
|
401
|
+
console.log();
|
|
402
|
+
console.log(` ${chalk3.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
|
|
403
|
+
console.log(
|
|
404
|
+
` ${chalk3.dim(
|
|
405
|
+
"This can download several hundred MB and take a few minutes."
|
|
406
|
+
)}`
|
|
407
|
+
);
|
|
408
|
+
console.log();
|
|
409
|
+
try {
|
|
410
|
+
execFileSync2(DOTNET, args, { stdio: "inherit" });
|
|
411
|
+
return true;
|
|
412
|
+
} catch {
|
|
413
|
+
console.error();
|
|
414
|
+
console.error(` ${chalk3.red("Workload install failed.")}`);
|
|
415
|
+
console.error(
|
|
416
|
+
` ${chalk3.dim(
|
|
417
|
+
"If this is a permissions error, your SDK is in a system location and needs elevation:"
|
|
418
|
+
)}`
|
|
419
|
+
);
|
|
420
|
+
console.error(` ${chalk3.cyan("sudo dotnet workload install maui")}`);
|
|
421
|
+
console.error();
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
var ensureMauiWorkload = async (opts = {}) => {
|
|
426
|
+
const dotnet = checkDotnetSdk();
|
|
427
|
+
if (dotnet.status === "missing") {
|
|
428
|
+
console.log();
|
|
429
|
+
console.log(` ${chalk3.yellow("!")} ${dotnet.name} \u2014 ${dotnet.detail}`);
|
|
430
|
+
if (dotnet.fix) {
|
|
431
|
+
console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(dotnet.fix)}`);
|
|
432
|
+
}
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
if (dotnet.status === "unknown") return true;
|
|
436
|
+
if (isMauiWorkloadInstalled()) return true;
|
|
437
|
+
console.log();
|
|
438
|
+
console.log(
|
|
439
|
+
` ${chalk3.yellow("!")} The .NET MAUI workload is required but not installed.`
|
|
440
|
+
);
|
|
441
|
+
const interactive = opts.interactive ?? isInteractive();
|
|
442
|
+
if (interactive) {
|
|
443
|
+
let install = false;
|
|
444
|
+
try {
|
|
445
|
+
const res = await prompts({
|
|
446
|
+
type: "confirm",
|
|
447
|
+
name: "install",
|
|
448
|
+
message: "Install the .NET MAUI workload now?",
|
|
449
|
+
initial: true
|
|
450
|
+
});
|
|
451
|
+
install = Boolean(res.install);
|
|
452
|
+
} catch {
|
|
453
|
+
install = false;
|
|
454
|
+
}
|
|
455
|
+
if (install) {
|
|
456
|
+
if (installWorkload(opts.csprojPath) && isMauiWorkloadInstalled()) {
|
|
457
|
+
console.log(` ${chalk3.green("\u2713")} MAUI workload installed.`);
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
console.log(
|
|
464
|
+
` ${chalk3.dim("run:")} ${chalk3.cyan("dotnet workload install maui")}`
|
|
465
|
+
);
|
|
466
|
+
console.log(` ${chalk3.dim("docs:")} ${chalk3.cyan(MAUI_DOCS)}`);
|
|
467
|
+
return false;
|
|
468
|
+
};
|
|
469
|
+
var printWorkloadHint = () => {
|
|
470
|
+
console.error();
|
|
471
|
+
console.error(
|
|
472
|
+
` ${chalk3.yellow("This looks like a missing .NET MAUI workload.")}`
|
|
473
|
+
);
|
|
474
|
+
console.error(
|
|
475
|
+
` ${chalk3.dim("fix: ")} ${chalk3.cyan("dotnet workload install maui")}`
|
|
476
|
+
);
|
|
477
|
+
console.error(
|
|
478
|
+
` ${chalk3.dim("check:")} ${chalk3.cyan("vidra doctor")}`
|
|
479
|
+
);
|
|
480
|
+
console.error();
|
|
481
|
+
};
|
|
482
|
+
|
|
239
483
|
// src/commands/build.ts
|
|
240
484
|
var VERSION = "0.1.0";
|
|
241
485
|
var TARGETS = {
|
|
@@ -248,35 +492,38 @@ var buildCommand = async (argv) => {
|
|
|
248
492
|
const targetName = args["target"] || detectPlatform();
|
|
249
493
|
console.log();
|
|
250
494
|
console.log(
|
|
251
|
-
` ${
|
|
495
|
+
` ${chalk4.bold.cyan("vidra build")} ${chalk4.dim(`v${VERSION}`)}`
|
|
252
496
|
);
|
|
253
497
|
console.log();
|
|
254
498
|
const target = TARGETS[targetName];
|
|
255
499
|
if (!target) {
|
|
256
500
|
const supported = Object.keys(TARGETS).join(", ");
|
|
257
501
|
console.error(
|
|
258
|
-
|
|
502
|
+
chalk4.red(
|
|
259
503
|
` Unsupported target: ${targetName}. Supported: ${supported}`
|
|
260
504
|
)
|
|
261
505
|
);
|
|
262
506
|
process.exit(1);
|
|
263
507
|
}
|
|
264
508
|
const project = detectProject(process.cwd());
|
|
265
|
-
console.log(` ${
|
|
266
|
-
console.log(` ${
|
|
509
|
+
console.log(` ${chalk4.dim("Project:")} ${chalk4.cyan(project.projectName)}`);
|
|
510
|
+
console.log(` ${chalk4.dim("Target:")} ${chalk4.cyan(target.name)} (${target.framework})`);
|
|
267
511
|
console.log();
|
|
512
|
+
if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
268
515
|
stepBuildUi(project, verbose);
|
|
269
516
|
stepCopyAssets(project);
|
|
270
517
|
const publishDir = stepDotnetPublish(project, target, verbose);
|
|
271
518
|
const bundlePath = target.findBundle(publishDir, project.projectName);
|
|
272
519
|
if (!bundlePath) {
|
|
273
520
|
console.error(
|
|
274
|
-
|
|
521
|
+
chalk4.red(` Could not find build artifact in ${publishDir}`)
|
|
275
522
|
);
|
|
276
523
|
process.exit(1);
|
|
277
524
|
}
|
|
278
525
|
console.log(
|
|
279
|
-
` ${
|
|
526
|
+
` ${chalk4.dim("Bundle:")} ${chalk4.cyan(path5.basename(bundlePath))}`
|
|
280
527
|
);
|
|
281
528
|
if (target.name === "macos") {
|
|
282
529
|
signMacAppBundleIfPossible(bundlePath, {
|
|
@@ -288,7 +535,7 @@ var buildCommand = async (argv) => {
|
|
|
288
535
|
const outputDir = path5.join(project.root, "dist");
|
|
289
536
|
fs4.ensureDirSync(outputDir);
|
|
290
537
|
console.log();
|
|
291
|
-
console.log(` ${
|
|
538
|
+
console.log(` ${chalk4.dim(`Packaging for ${target.name}...`)}`);
|
|
292
539
|
const startPkg = Date.now();
|
|
293
540
|
let outputPath;
|
|
294
541
|
try {
|
|
@@ -298,24 +545,24 @@ var buildCommand = async (argv) => {
|
|
|
298
545
|
});
|
|
299
546
|
} catch (e) {
|
|
300
547
|
const err = e;
|
|
301
|
-
console.error(
|
|
302
|
-
console.error(
|
|
548
|
+
console.error(chalk4.red(` Packaging failed.`));
|
|
549
|
+
console.error(chalk4.dim(err.stderr?.toString() || err.message));
|
|
303
550
|
process.exit(1);
|
|
304
551
|
}
|
|
305
552
|
const pkgTime = ((Date.now() - startPkg) / 1e3).toFixed(1);
|
|
306
553
|
const sizeBytes = fs4.statSync(outputPath).size;
|
|
307
554
|
const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(1);
|
|
308
555
|
console.log(
|
|
309
|
-
` ${
|
|
556
|
+
` ${chalk4.green(">")} ${path5.basename(outputPath)} ${chalk4.dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
|
|
310
557
|
);
|
|
311
558
|
console.log();
|
|
312
559
|
console.log(
|
|
313
|
-
` ${
|
|
560
|
+
` ${chalk4.green("Done!")} Output: ${chalk4.cyan(path5.relative(project.root, outputPath))}`
|
|
314
561
|
);
|
|
315
562
|
console.log();
|
|
316
563
|
};
|
|
317
564
|
var stepBuildUi = (project, verbose) => {
|
|
318
|
-
console.log(` ${
|
|
565
|
+
console.log(` ${chalk4.dim("Building UI...")}`);
|
|
319
566
|
const start = Date.now();
|
|
320
567
|
try {
|
|
321
568
|
execSync2("npm run build", {
|
|
@@ -324,19 +571,19 @@ var stepBuildUi = (project, verbose) => {
|
|
|
324
571
|
});
|
|
325
572
|
} catch (e) {
|
|
326
573
|
const err = e;
|
|
327
|
-
console.error(
|
|
328
|
-
console.error(
|
|
574
|
+
console.error(chalk4.red(" Vite build failed."));
|
|
575
|
+
console.error(chalk4.dim(err.stderr?.toString() || err.message));
|
|
329
576
|
process.exit(1);
|
|
330
577
|
}
|
|
331
578
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
332
|
-
console.log(` ${
|
|
579
|
+
console.log(` ${chalk4.green(">")} Vite build complete ${chalk4.dim(`(${elapsed}s)`)}`);
|
|
333
580
|
console.log();
|
|
334
581
|
};
|
|
335
582
|
var stepCopyAssets = (project) => {
|
|
336
|
-
console.log(` ${
|
|
583
|
+
console.log(` ${chalk4.dim("Copying assets to host project...")}`);
|
|
337
584
|
const viteDist = path5.join(project.uiDir, "dist");
|
|
338
585
|
if (!fs4.existsSync(viteDist)) {
|
|
339
|
-
console.error(
|
|
586
|
+
console.error(chalk4.red(` ui/dist not found. Vite build may have failed.`));
|
|
340
587
|
process.exit(1);
|
|
341
588
|
}
|
|
342
589
|
const wwwroot = path5.join(project.hostDir, "Resources", "Raw", "wwwroot");
|
|
@@ -344,7 +591,7 @@ var stepCopyAssets = (project) => {
|
|
|
344
591
|
fs4.copySync(viteDist, wwwroot);
|
|
345
592
|
const fileCount = countFiles(wwwroot);
|
|
346
593
|
console.log(
|
|
347
|
-
` ${
|
|
594
|
+
` ${chalk4.green(">")} ${fileCount} files -> ${chalk4.dim("Resources/Raw/wwwroot/")}`
|
|
348
595
|
);
|
|
349
596
|
console.log();
|
|
350
597
|
};
|
|
@@ -361,7 +608,7 @@ var countFiles = (dir) => {
|
|
|
361
608
|
};
|
|
362
609
|
var stepDotnetPublish = (project, target, verbose) => {
|
|
363
610
|
console.log(
|
|
364
|
-
` ${
|
|
611
|
+
` ${chalk4.dim(`Publishing .NET host (${target.framework})...`)}`
|
|
365
612
|
);
|
|
366
613
|
const start = Date.now();
|
|
367
614
|
const extraArgs = target.extraPublishArgs ?? "-p:CreatePackage=false";
|
|
@@ -375,13 +622,15 @@ var stepDotnetPublish = (project, target, verbose) => {
|
|
|
375
622
|
);
|
|
376
623
|
} catch (e) {
|
|
377
624
|
const err = e;
|
|
378
|
-
|
|
379
|
-
console.error(
|
|
625
|
+
const output = err.stderr?.toString() || err.message;
|
|
626
|
+
console.error(chalk4.red(" dotnet publish failed."));
|
|
627
|
+
console.error(chalk4.dim(output));
|
|
628
|
+
if (looksLikeMissingWorkload(output)) printWorkloadHint();
|
|
380
629
|
process.exit(1);
|
|
381
630
|
}
|
|
382
631
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
383
632
|
console.log(
|
|
384
|
-
` ${
|
|
633
|
+
` ${chalk4.green(">")} dotnet publish complete ${chalk4.dim(`(${elapsed}s)`)}`
|
|
385
634
|
);
|
|
386
635
|
const publishDir = path5.join(
|
|
387
636
|
project.hostDir,
|
|
@@ -395,9 +644,9 @@ var stepDotnetPublish = (project, target, verbose) => {
|
|
|
395
644
|
// src/commands/dev.ts
|
|
396
645
|
import path6 from "path";
|
|
397
646
|
import fs5 from "fs-extra";
|
|
398
|
-
import { execFileSync as
|
|
647
|
+
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
399
648
|
import { request } from "http";
|
|
400
|
-
import
|
|
649
|
+
import chalk5 from "chalk";
|
|
401
650
|
var VERSION2 = "0.1.0";
|
|
402
651
|
var POLL_INTERVAL_MS = 500;
|
|
403
652
|
var POLL_TIMEOUT_MS = 3e4;
|
|
@@ -422,7 +671,7 @@ var devCommand = async (argv) => {
|
|
|
422
671
|
if (!target) {
|
|
423
672
|
const supported = Object.keys(TARGETS2).join(", ");
|
|
424
673
|
console.error(
|
|
425
|
-
|
|
674
|
+
chalk5.red(
|
|
426
675
|
` Unsupported target: ${targetName}. Supported: ${supported}`
|
|
427
676
|
)
|
|
428
677
|
);
|
|
@@ -430,6 +679,9 @@ var devCommand = async (argv) => {
|
|
|
430
679
|
}
|
|
431
680
|
ensureTargetMatchesHostOs(target.name);
|
|
432
681
|
const project = detectProject(process.cwd());
|
|
682
|
+
if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
433
685
|
const session = new DevSession(project, target, viteUrl, verbose);
|
|
434
686
|
await session.run();
|
|
435
687
|
};
|
|
@@ -450,11 +702,11 @@ var DevSession = class {
|
|
|
450
702
|
async run() {
|
|
451
703
|
this.installSignalHandlers();
|
|
452
704
|
console.log();
|
|
453
|
-
console.log(` ${
|
|
705
|
+
console.log(` ${chalk5.bold.cyan("vidra dev")} ${chalk5.dim(`v${VERSION2}`)}`);
|
|
454
706
|
console.log();
|
|
455
|
-
console.log(` ${
|
|
707
|
+
console.log(` ${chalk5.dim("Project:")} ${chalk5.cyan(this.project.projectName)}`);
|
|
456
708
|
console.log(
|
|
457
|
-
` ${
|
|
709
|
+
` ${chalk5.dim("Target:")} ${chalk5.cyan(this.target.name)} (${this.target.framework})`
|
|
458
710
|
);
|
|
459
711
|
console.log();
|
|
460
712
|
const vite = this.startVite();
|
|
@@ -462,13 +714,13 @@ var DevSession = class {
|
|
|
462
714
|
await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
|
|
463
715
|
} catch (error) {
|
|
464
716
|
console.error(
|
|
465
|
-
|
|
717
|
+
chalk5.red(
|
|
466
718
|
` ${error.message}`
|
|
467
719
|
)
|
|
468
720
|
);
|
|
469
721
|
this.shutdown(1);
|
|
470
722
|
}
|
|
471
|
-
console.log(` ${
|
|
723
|
+
console.log(` ${chalk5.dim("Vite:")} ${chalk5.cyan(this.viteUrl)}`);
|
|
472
724
|
console.log();
|
|
473
725
|
const host = this.target.name === "macos" ? this.launchMacosHost() : this.launchWindowsHost();
|
|
474
726
|
await waitForExit(vite, host);
|
|
@@ -483,7 +735,7 @@ var DevSession = class {
|
|
|
483
735
|
});
|
|
484
736
|
}
|
|
485
737
|
startVite() {
|
|
486
|
-
console.log(` ${
|
|
738
|
+
console.log(` ${chalk5.dim("Starting Vite dev server...")}`);
|
|
487
739
|
const vite = spawn(NPM_COMMAND, ["run", "dev"], {
|
|
488
740
|
cwd: this.project.uiDir,
|
|
489
741
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -492,10 +744,10 @@ var DevSession = class {
|
|
|
492
744
|
}
|
|
493
745
|
launchMacosHost() {
|
|
494
746
|
console.log(
|
|
495
|
-
` ${
|
|
747
|
+
` ${chalk5.dim(`Building MAUI host (${this.target.framework})...`)}`
|
|
496
748
|
);
|
|
497
749
|
try {
|
|
498
|
-
|
|
750
|
+
execFileSync3(
|
|
499
751
|
DOTNET_COMMAND,
|
|
500
752
|
[
|
|
501
753
|
"build",
|
|
@@ -511,8 +763,10 @@ var DevSession = class {
|
|
|
511
763
|
}
|
|
512
764
|
);
|
|
513
765
|
} catch (error) {
|
|
514
|
-
|
|
515
|
-
console.error(
|
|
766
|
+
const output = formatExecError2(error);
|
|
767
|
+
console.error(chalk5.red(" MAUI build failed."));
|
|
768
|
+
console.error(chalk5.dim(output));
|
|
769
|
+
if (looksLikeMissingWorkload(output)) printWorkloadHint();
|
|
516
770
|
process.exit(1);
|
|
517
771
|
}
|
|
518
772
|
const appBundle = findMacAppBundle(
|
|
@@ -522,7 +776,7 @@ var DevSession = class {
|
|
|
522
776
|
);
|
|
523
777
|
if (!appBundle) {
|
|
524
778
|
console.error(
|
|
525
|
-
|
|
779
|
+
chalk5.red(
|
|
526
780
|
` Could not find .app bundle in ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
|
|
527
781
|
)
|
|
528
782
|
);
|
|
@@ -536,11 +790,11 @@ var DevSession = class {
|
|
|
536
790
|
const binary = findMacExecutable(appBundle);
|
|
537
791
|
if (!binary) {
|
|
538
792
|
console.error(
|
|
539
|
-
|
|
793
|
+
chalk5.red(` Could not find the app executable in ${appBundle}.`)
|
|
540
794
|
);
|
|
541
795
|
process.exit(1);
|
|
542
796
|
}
|
|
543
|
-
console.log(` ${
|
|
797
|
+
console.log(` ${chalk5.dim("Launching host...")}`);
|
|
544
798
|
const host = spawn(binary, [], {
|
|
545
799
|
cwd: this.project.root,
|
|
546
800
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -549,7 +803,7 @@ var DevSession = class {
|
|
|
549
803
|
return this.registerChild(host, "host", path6.basename(binary));
|
|
550
804
|
}
|
|
551
805
|
launchWindowsHost() {
|
|
552
|
-
console.log(` ${
|
|
806
|
+
console.log(` ${chalk5.dim("Launching host...")}`);
|
|
553
807
|
const host = spawn(
|
|
554
808
|
DOTNET_COMMAND,
|
|
555
809
|
[
|
|
@@ -578,21 +832,21 @@ var DevSession = class {
|
|
|
578
832
|
if (tag === "ui") {
|
|
579
833
|
const exitCode = code ?? 1;
|
|
580
834
|
console.error(
|
|
581
|
-
|
|
835
|
+
chalk5.red(`
|
|
582
836
|
${label} exited with code ${exitCode}.`)
|
|
583
837
|
);
|
|
584
838
|
this.shutdown(exitCode);
|
|
585
839
|
return;
|
|
586
840
|
}
|
|
587
841
|
if (code !== null && code !== 0) {
|
|
588
|
-
console.error(
|
|
842
|
+
console.error(chalk5.red(`
|
|
589
843
|
${label} exited with code ${code}.`));
|
|
590
844
|
}
|
|
591
845
|
this.shutdown(code ?? 0);
|
|
592
846
|
});
|
|
593
847
|
child.on("error", (error) => {
|
|
594
848
|
if (this.shuttingDown) return;
|
|
595
|
-
console.error(
|
|
849
|
+
console.error(chalk5.red(`
|
|
596
850
|
Failed to start ${label}: ${error.message}`));
|
|
597
851
|
this.shutdown(1);
|
|
598
852
|
});
|
|
@@ -609,12 +863,12 @@ var DevSession = class {
|
|
|
609
863
|
};
|
|
610
864
|
var ensureTargetMatchesHostOs = (targetName) => {
|
|
611
865
|
if (targetName === "macos" && process.platform !== "darwin") {
|
|
612
|
-
console.error(
|
|
866
|
+
console.error(chalk5.red(" The macOS dev target can only run on macOS."));
|
|
613
867
|
process.exit(1);
|
|
614
868
|
}
|
|
615
869
|
if (targetName === "windows" && process.platform !== "win32") {
|
|
616
870
|
console.error(
|
|
617
|
-
|
|
871
|
+
chalk5.red(" The Windows dev target can only run on Windows.")
|
|
618
872
|
);
|
|
619
873
|
process.exit(1);
|
|
620
874
|
}
|
|
@@ -701,7 +955,7 @@ var killChild = (child) => {
|
|
|
701
955
|
if (!child.pid || child.exitCode !== null) return;
|
|
702
956
|
if (process.platform === "win32") {
|
|
703
957
|
try {
|
|
704
|
-
|
|
958
|
+
execFileSync3("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
705
959
|
stdio: "ignore"
|
|
706
960
|
});
|
|
707
961
|
} catch {
|
|
@@ -723,25 +977,27 @@ var formatExecError2 = (error) => {
|
|
|
723
977
|
};
|
|
724
978
|
|
|
725
979
|
// src/cli.ts
|
|
726
|
-
var VERSION3 = "0.1.
|
|
980
|
+
var VERSION3 = "0.1.3";
|
|
727
981
|
var printHelp = () => {
|
|
728
982
|
console.log(`
|
|
729
|
-
${
|
|
983
|
+
${chalk6.bold("vidra")} ${chalk6.dim(`v${VERSION3}`)}
|
|
730
984
|
|
|
731
|
-
${
|
|
985
|
+
${chalk6.dim("Usage:")}
|
|
732
986
|
vidra <command> [options]
|
|
733
987
|
|
|
734
|
-
${
|
|
988
|
+
${chalk6.dim("Commands:")}
|
|
735
989
|
dev Start the development environment
|
|
736
990
|
build Build and package the application for distribution
|
|
991
|
+
doctor Check that your environment is set up to build Vidra apps
|
|
737
992
|
help Show this help message
|
|
738
993
|
|
|
739
|
-
${
|
|
740
|
-
vidra dev ${
|
|
741
|
-
vidra dev --target windows ${
|
|
742
|
-
vidra build ${
|
|
743
|
-
vidra build --target macos ${
|
|
744
|
-
vidra build --verbose ${
|
|
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")}
|
|
745
1001
|
`);
|
|
746
1002
|
};
|
|
747
1003
|
var main = async () => {
|
|
@@ -754,6 +1010,9 @@ var main = async () => {
|
|
|
754
1010
|
case "build":
|
|
755
1011
|
await buildCommand(args.slice(1));
|
|
756
1012
|
break;
|
|
1013
|
+
case "doctor":
|
|
1014
|
+
process.exit(await runDoctor());
|
|
1015
|
+
break;
|
|
757
1016
|
case "help":
|
|
758
1017
|
case "--help":
|
|
759
1018
|
case "-h":
|
|
@@ -765,13 +1024,13 @@ var main = async () => {
|
|
|
765
1024
|
console.log(VERSION3);
|
|
766
1025
|
break;
|
|
767
1026
|
default:
|
|
768
|
-
console.error(
|
|
1027
|
+
console.error(chalk6.red(` Unknown command: ${command}
|
|
769
1028
|
`));
|
|
770
1029
|
printHelp();
|
|
771
1030
|
process.exit(1);
|
|
772
1031
|
}
|
|
773
1032
|
};
|
|
774
1033
|
main().catch((e) => {
|
|
775
|
-
console.error(
|
|
1034
|
+
console.error(chalk6.red(e.message));
|
|
776
1035
|
process.exit(1);
|
|
777
1036
|
});
|