create-vidra-app 0.1.1 → 0.1.3
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 +171 -23
- package/package.json +1 -1
- package/templates/react-vite/NuGet.Config +1 -1
- package/templates/react-vite/README.md +12 -2
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
|
});
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import prompts2 from "prompts";
|
|
3
|
+
import chalk3 from "chalk";
|
|
4
4
|
import fs2 from "fs-extra";
|
|
5
5
|
import path2 from "path";
|
|
6
6
|
import { randomUUID } from "crypto";
|
|
@@ -112,6 +112,140 @@ var applyReplacements = (str, replacements) => {
|
|
|
112
112
|
return str;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
// src/doctor.ts
|
|
116
|
+
import { execFileSync } from "child_process";
|
|
117
|
+
import prompts from "prompts";
|
|
118
|
+
import chalk2 from "chalk";
|
|
119
|
+
var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
|
|
120
|
+
var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
|
|
121
|
+
var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
|
|
122
|
+
var run = (cmd, args) => {
|
|
123
|
+
try {
|
|
124
|
+
const stdout = execFileSync(cmd, args, {
|
|
125
|
+
encoding: "utf-8",
|
|
126
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
127
|
+
});
|
|
128
|
+
return { found: true, ok: true, stdout: stdout ?? "", stderr: "" };
|
|
129
|
+
} catch (e) {
|
|
130
|
+
const err = e;
|
|
131
|
+
return {
|
|
132
|
+
found: err.code !== "ENOENT",
|
|
133
|
+
ok: false,
|
|
134
|
+
stdout: bufToStr(err.stdout),
|
|
135
|
+
stderr: bufToStr(err.stderr)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var hasNet10Sdk = (listSdksOutput) => listSdksOutput.split(/\r?\n/).some((line) => /^10\./.test(line.trim()));
|
|
140
|
+
var newestNet10Sdk = (listSdksOutput) => listSdksOutput.split(/\r?\n/).map((line) => line.trim().split(/\s+/)[0]).filter((v) => /^10\./.test(v)).sort(
|
|
141
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
|
|
142
|
+
).pop();
|
|
143
|
+
var outputMentionsMaui = (workloadListOutput) => /\bmaui\b/i.test(workloadListOutput);
|
|
144
|
+
var checkDotnetSdk = () => {
|
|
145
|
+
const res = run(DOTNET, ["--list-sdks"]);
|
|
146
|
+
if (!res.found) {
|
|
147
|
+
return {
|
|
148
|
+
name: ".NET SDK",
|
|
149
|
+
status: "missing",
|
|
150
|
+
detail: "`dotnet` was not found on your PATH",
|
|
151
|
+
fix: "Install the .NET 10 SDK \u2014 https://dotnet.microsoft.com/download"
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (!res.ok && !res.stdout) {
|
|
155
|
+
return {
|
|
156
|
+
name: ".NET SDK",
|
|
157
|
+
status: "unknown",
|
|
158
|
+
detail: "could not run `dotnet --list-sdks`"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (hasNet10Sdk(res.stdout)) {
|
|
162
|
+
const newest = newestNet10Sdk(res.stdout);
|
|
163
|
+
return {
|
|
164
|
+
name: ".NET SDK",
|
|
165
|
+
status: "ok",
|
|
166
|
+
detail: newest ? `found ${newest}` : "found 10.x"
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
name: ".NET SDK",
|
|
171
|
+
status: "missing",
|
|
172
|
+
detail: "no 10.x SDK installed",
|
|
173
|
+
fix: "Install the .NET 10 SDK \u2014 https://dotnet.microsoft.com/download"
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
var isMauiWorkloadInstalled = () => outputMentionsMaui(run(DOTNET, ["workload", "list"]).stdout);
|
|
177
|
+
var isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
178
|
+
var installWorkload = (csprojPath) => {
|
|
179
|
+
const args = csprojPath ? ["workload", "restore", csprojPath] : ["workload", "install", "maui"];
|
|
180
|
+
console.log();
|
|
181
|
+
console.log(` ${chalk2.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
|
|
182
|
+
console.log(
|
|
183
|
+
` ${chalk2.dim(
|
|
184
|
+
"This can download several hundred MB and take a few minutes."
|
|
185
|
+
)}`
|
|
186
|
+
);
|
|
187
|
+
console.log();
|
|
188
|
+
try {
|
|
189
|
+
execFileSync(DOTNET, args, { stdio: "inherit" });
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
console.error();
|
|
193
|
+
console.error(` ${chalk2.red("Workload install failed.")}`);
|
|
194
|
+
console.error(
|
|
195
|
+
` ${chalk2.dim(
|
|
196
|
+
"If this is a permissions error, your SDK is in a system location and needs elevation:"
|
|
197
|
+
)}`
|
|
198
|
+
);
|
|
199
|
+
console.error(` ${chalk2.cyan("sudo dotnet workload install maui")}`);
|
|
200
|
+
console.error();
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var ensureMauiWorkload = async (opts = {}) => {
|
|
205
|
+
const dotnet = checkDotnetSdk();
|
|
206
|
+
if (dotnet.status === "missing") {
|
|
207
|
+
console.log();
|
|
208
|
+
console.log(` ${chalk2.yellow("!")} ${dotnet.name} \u2014 ${dotnet.detail}`);
|
|
209
|
+
if (dotnet.fix) {
|
|
210
|
+
console.log(` ${chalk2.dim("fix:")} ${chalk2.cyan(dotnet.fix)}`);
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (dotnet.status === "unknown") return true;
|
|
215
|
+
if (isMauiWorkloadInstalled()) return true;
|
|
216
|
+
console.log();
|
|
217
|
+
console.log(
|
|
218
|
+
` ${chalk2.yellow("!")} The .NET MAUI workload is required but not installed.`
|
|
219
|
+
);
|
|
220
|
+
const interactive = opts.interactive ?? isInteractive();
|
|
221
|
+
if (interactive) {
|
|
222
|
+
let install = false;
|
|
223
|
+
try {
|
|
224
|
+
const res = await prompts({
|
|
225
|
+
type: "confirm",
|
|
226
|
+
name: "install",
|
|
227
|
+
message: "Install the .NET MAUI workload now?",
|
|
228
|
+
initial: true
|
|
229
|
+
});
|
|
230
|
+
install = Boolean(res.install);
|
|
231
|
+
} catch {
|
|
232
|
+
install = false;
|
|
233
|
+
}
|
|
234
|
+
if (install) {
|
|
235
|
+
if (installWorkload(opts.csprojPath) && isMauiWorkloadInstalled()) {
|
|
236
|
+
console.log(` ${chalk2.green("\u2713")} MAUI workload installed.`);
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
console.log(
|
|
243
|
+
` ${chalk2.dim("run:")} ${chalk2.cyan("dotnet workload install maui")}`
|
|
244
|
+
);
|
|
245
|
+
console.log(` ${chalk2.dim("docs:")} ${chalk2.cyan(MAUI_DOCS)}`);
|
|
246
|
+
return false;
|
|
247
|
+
};
|
|
248
|
+
|
|
115
249
|
// src/index.ts
|
|
116
250
|
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
117
251
|
var CLI_ROOT = path2.resolve(__dirname, "..");
|
|
@@ -124,13 +258,13 @@ var VIDRA_VERSION = "0.1.0";
|
|
|
124
258
|
var SDK_VERSION = "0.1.0";
|
|
125
259
|
var main = async () => {
|
|
126
260
|
console.log();
|
|
127
|
-
console.log(
|
|
128
|
-
console.log(
|
|
261
|
+
console.log(chalk3.bold(" create-vidra-app"));
|
|
262
|
+
console.log(chalk3.dim(" Scaffold a new Vidra application\n"));
|
|
129
263
|
const args = parseArgs(process.argv);
|
|
130
264
|
let projectDir = args._[0];
|
|
131
265
|
let appId = args["app-id"];
|
|
132
266
|
if (!projectDir) {
|
|
133
|
-
const res = await
|
|
267
|
+
const res = await prompts2(
|
|
134
268
|
{
|
|
135
269
|
type: "text",
|
|
136
270
|
name: "projectDir",
|
|
@@ -147,7 +281,7 @@ var main = async () => {
|
|
|
147
281
|
const appTitle = toTitleCase(projectDir);
|
|
148
282
|
const appGuid = randomUUID().toUpperCase();
|
|
149
283
|
if (!appId) {
|
|
150
|
-
const res = await
|
|
284
|
+
const res = await prompts2(
|
|
151
285
|
{
|
|
152
286
|
type: "text",
|
|
153
287
|
name: "appId",
|
|
@@ -162,7 +296,7 @@ var main = async () => {
|
|
|
162
296
|
const root = path2.resolve(projectDir);
|
|
163
297
|
if (fs2.existsSync(root) && fs2.readdirSync(root).length > 0) {
|
|
164
298
|
console.error(
|
|
165
|
-
|
|
299
|
+
chalk3.red(
|
|
166
300
|
`
|
|
167
301
|
Directory "${projectDir}" already exists and is not empty.
|
|
168
302
|
`
|
|
@@ -171,13 +305,13 @@ var main = async () => {
|
|
|
171
305
|
process.exit(1);
|
|
172
306
|
}
|
|
173
307
|
console.log();
|
|
174
|
-
console.log(` ${
|
|
175
|
-
console.log(` ${
|
|
176
|
-
console.log(` ${
|
|
308
|
+
console.log(` ${chalk3.dim("Project:")} ${chalk3.cyan(projectName)}`);
|
|
309
|
+
console.log(` ${chalk3.dim("Directory:")} ${chalk3.cyan(root)}`);
|
|
310
|
+
console.log(` ${chalk3.dim("App ID:")} ${chalk3.cyan(appId)}`);
|
|
177
311
|
console.log();
|
|
178
312
|
const isMonorepo = fs2.existsSync(path2.join(LOCAL_SDK_DIR, "package.json"));
|
|
179
313
|
const localFeedExists = isMonorepo && fs2.existsSync(LOCAL_FEED_DIR);
|
|
180
|
-
const
|
|
314
|
+
const localFeedSource = localFeedExists ? ` <add key="vidra-local" value="${toTextPath(LOCAL_FEED_DIR)}" />` : "";
|
|
181
315
|
const cliRef = isMonorepo ? `file:${toTextPath(LOCAL_CLI_DIR)}` : `^${VIDRA_VERSION}`;
|
|
182
316
|
const sdkRef = isMonorepo ? `file:${toTextPath(LOCAL_SDK_DIR)}` : `^${SDK_VERSION}`;
|
|
183
317
|
const replacements = {
|
|
@@ -189,55 +323,69 @@ var main = async () => {
|
|
|
189
323
|
"{{cliVersion}}": cliRef,
|
|
190
324
|
"{{vidraVersion}}": VIDRA_VERSION,
|
|
191
325
|
"{{sdkVersion}}": sdkRef,
|
|
192
|
-
"{{
|
|
326
|
+
"{{localFeedSource}}": localFeedSource
|
|
193
327
|
};
|
|
194
328
|
const templateDir = path2.join(TEMPLATES_DIR, "react-vite");
|
|
195
329
|
await scaffoldDir(templateDir, root, replacements);
|
|
196
|
-
console.log(
|
|
330
|
+
console.log(chalk3.dim(" Creating solution..."));
|
|
197
331
|
exec(`dotnet new sln -n ${projectName} --force`, root);
|
|
198
332
|
const slnFile = fs2.existsSync(path2.join(root, `${projectName}.slnx`)) ? `${projectName}.slnx` : `${projectName}.sln`;
|
|
199
333
|
exec(
|
|
200
334
|
`dotnet sln ${slnFile} add src/${projectName}.Host/${projectName}.Host.csproj`,
|
|
201
335
|
root
|
|
202
336
|
);
|
|
203
|
-
console.log(
|
|
337
|
+
console.log(chalk3.dim(" Installing npm dependencies..."));
|
|
204
338
|
const uiDir = path2.join(root, "ui");
|
|
205
339
|
const rootNpmOk = tryExec("npm install", root);
|
|
206
340
|
const uiNpmOk = tryExec("npm install", uiDir);
|
|
207
341
|
const npmOk = rootNpmOk && uiNpmOk;
|
|
208
342
|
console.log();
|
|
209
|
-
console.log(
|
|
343
|
+
console.log(chalk3.green(" Done! ") + "Your Vidra app is ready.\n");
|
|
210
344
|
if (localFeedExists) {
|
|
211
345
|
console.log(
|
|
212
|
-
|
|
346
|
+
chalk3.dim(" NuGet:") + " local feed \u2192 " + chalk3.cyan(LOCAL_FEED_DIR)
|
|
213
347
|
);
|
|
214
348
|
} else if (isMonorepo) {
|
|
215
349
|
console.log(
|
|
216
|
-
|
|
350
|
+
chalk3.yellow(" Note: ") + "Local NuGet feed not found. Run " + chalk3.cyan("./pack-local.sh") + " in the Vidra repo, then update NuGet.Config."
|
|
217
351
|
);
|
|
218
352
|
}
|
|
219
353
|
if (isMonorepo) {
|
|
220
354
|
console.log(
|
|
221
|
-
|
|
355
|
+
chalk3.dim(" npm: ") + " @vidra-dev/sdk \u2192 " + chalk3.cyan(LOCAL_SDK_DIR)
|
|
222
356
|
);
|
|
223
357
|
console.log(
|
|
224
|
-
|
|
358
|
+
chalk3.dim(" npm: ") + " create-vidra-app \u2192 " + chalk3.cyan(LOCAL_CLI_DIR)
|
|
225
359
|
);
|
|
226
360
|
}
|
|
227
361
|
console.log();
|
|
228
362
|
if (!npmOk) {
|
|
229
363
|
console.log(
|
|
230
|
-
|
|
364
|
+
chalk3.yellow(" Note: ") + "`npm install` had errors. Re-run " + chalk3.cyan("npm install") + " in the project root and in " + chalk3.cyan("ui/") + " to retry.\n"
|
|
231
365
|
);
|
|
232
366
|
}
|
|
233
|
-
|
|
367
|
+
const hostCsproj = path2.join(
|
|
368
|
+
root,
|
|
369
|
+
"src",
|
|
370
|
+
`${projectName}.Host`,
|
|
371
|
+
`${projectName}.Host.csproj`
|
|
372
|
+
);
|
|
373
|
+
const prereqsReady = await ensureMauiWorkload({ csprojPath: hostCsproj });
|
|
374
|
+
console.log(chalk3.bold(" Next steps:\n"));
|
|
234
375
|
console.log(` cd ${projectDir}`);
|
|
235
376
|
console.log(
|
|
236
|
-
` npm run dev ${
|
|
377
|
+
` npm run dev ${chalk3.dim("# starts Vite + MAUI host together")}`
|
|
237
378
|
);
|
|
379
|
+
if (!prereqsReady) {
|
|
380
|
+
console.log(
|
|
381
|
+
` ${chalk3.dim("# tip: run")} ${chalk3.cyan(
|
|
382
|
+
"vidra doctor"
|
|
383
|
+
)} ${chalk3.dim("to verify your setup first")}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
238
386
|
console.log();
|
|
239
387
|
};
|
|
240
388
|
main().catch((e) => {
|
|
241
|
-
console.error(
|
|
389
|
+
console.error(chalk3.red(e.message));
|
|
242
390
|
process.exit(1);
|
|
243
391
|
});
|
package/package.json
CHANGED
|
@@ -6,9 +6,19 @@ A cross-platform application built with [Vidra](https://github.com/user/vidra)
|
|
|
6
6
|
|
|
7
7
|
### Prerequisites
|
|
8
8
|
|
|
9
|
-
- [.NET 10 SDK](https://dotnet.microsoft.com/download)
|
|
9
|
+
- [.NET 10 SDK](https://dotnet.microsoft.com/download)
|
|
10
|
+
- The .NET MAUI workload: `dotnet workload install maui`
|
|
10
11
|
- [Node.js](https://nodejs.org/) 18+
|
|
11
|
-
-
|
|
12
|
+
- macOS targets require Xcode; Windows targets must be built on Windows
|
|
13
|
+
|
|
14
|
+
Not sure if you're set up? Run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
vidra doctor
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It checks your .NET SDK, the MAUI workload, and (on macOS) Xcode, and prints the
|
|
21
|
+
exact command to fix anything that's missing.
|
|
12
22
|
|
|
13
23
|
### Development
|
|
14
24
|
|