create-vidra-app 0.1.6 → 0.1.8

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 CHANGED
@@ -20,13 +20,16 @@ cd my-app
20
20
  npm run dev # starts Vite + the native host together
21
21
  ```
22
22
 
23
- This package also installs the `vidra` CLI used by scaffolded apps:
23
+ Scaffolded apps get the `vidra` CLI as a local dev dependency — there's no
24
+ global `vidra` to install. Run it from inside your project via the npm scripts
25
+ or `npx`:
24
26
 
25
27
  ```bash
26
- vidra dev # start Vite + native host
27
- vidra dev --target windows # run the Windows host
28
- vidra build --target macos # build + package a macOS .dmg
29
- vidra doctor # check your .NET / MAUI / Xcode setup
28
+ npm run dev # start Vite + native host
29
+ npm run build # build + package for distribution
30
+ npm run doctor # check your .NET / MAUI / Xcode setup
31
+ npx vidra dev --target windows # run a specific desktop target
32
+ npx vidra build --target macos # build + package a macOS .dmg
30
33
  ```
31
34
 
32
35
  ## Prerequisites
@@ -38,7 +41,7 @@ vidra doctor # check your .NET / MAUI / Xcode setup
38
41
 
39
42
  If the MAUI workload is missing, `create-vidra-app` will detect it after
40
43
  scaffolding and offer to install it for you. You can re-check at any time with
41
- `vidra doctor`.
44
+ `npm run doctor`.
42
45
 
43
46
  ## License
44
47
 
package/dist/cli.js CHANGED
@@ -304,44 +304,74 @@ var formatExecError = (error) => {
304
304
 
305
305
  // src/targets/windows.ts
306
306
  import path4 from "path";
307
+ import os2 from "os";
307
308
  import fs3 from "fs-extra";
309
+ import { execFileSync as execFileSync2 } from "child_process";
308
310
  var windowsTarget = {
309
311
  name: "windows",
310
312
  framework: "net10.0-windows10.0.19041.0",
311
- extraPublishArgs: "-p:RuntimeIdentifierOverride=win-x64 -p:WindowsPackageType=MSIX",
313
+ // `RuntimeIdentifierOverride` (rather than `-r`/`RuntimeIdentifier`) is the
314
+ // MAUI-recommended way to set the Windows RID — it sidesteps WindowsAppSDK
315
+ // issue #3337, which otherwise pulls in the wrong packaging assets.
316
+ extraPublishArgs: "-p:WindowsPackageType=None -p:SelfContained=true -p:WindowsAppSDKSelfContained=true -p:RuntimeIdentifierOverride=win-x64",
312
317
  findBundle(publishDir, _projectName) {
313
- const appPackagesDir = path4.join(publishDir, "win-x64", "AppPackages");
314
- if (!fs3.existsSync(appPackagesDir)) return null;
315
- for (const entry of fs3.readdirSync(appPackagesDir, {
316
- withFileTypes: true
317
- })) {
318
- if (!entry.isDirectory()) continue;
319
- const subDir = path4.join(appPackagesDir, entry.name);
320
- for (const file of fs3.readdirSync(subDir)) {
321
- if (file.endsWith(".msix")) {
322
- return path4.join(subDir, file);
323
- }
324
- }
325
- }
326
- return null;
318
+ const preferred = path4.join(publishDir, "win-x64", "publish");
319
+ if (dirContainsExe(preferred)) return preferred;
320
+ return findDirWithExe(publishDir);
327
321
  },
328
- async package(msixPath, outputDir, meta) {
329
- const outName = `${meta.projectName}-${meta.displayVersion}-windows.msix`;
322
+ async package(publishOutputDir, outputDir, meta) {
323
+ const outName = `${meta.projectName}-${meta.displayVersion}-windows.zip`;
330
324
  const outPath = path4.join(outputDir, outName);
331
- fs3.copySync(msixPath, outPath, { overwrite: true });
325
+ if (fs3.existsSync(outPath)) fs3.removeSync(outPath);
326
+ const staging = fs3.mkdtempSync(path4.join(os2.tmpdir(), "vidra-zip-"));
327
+ const stagedApp = path4.join(staging, meta.projectName);
328
+ try {
329
+ fs3.copySync(publishOutputDir, stagedApp);
330
+ execFileSync2(
331
+ "powershell",
332
+ [
333
+ "-NoProfile",
334
+ "-NonInteractive",
335
+ "-Command",
336
+ `Compress-Archive -Path "${stagedApp}" -DestinationPath "${outPath}" -Force`
337
+ ],
338
+ { stdio: "pipe" }
339
+ );
340
+ } finally {
341
+ fs3.removeSync(staging);
342
+ }
332
343
  return outPath;
333
344
  }
334
345
  };
346
+ var dirContainsExe = (dir) => fs3.existsSync(dir) && fs3.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile() && e.name.toLowerCase().endsWith(".exe"));
347
+ var findDirWithExe = (root) => {
348
+ if (!fs3.existsSync(root)) return null;
349
+ let fallback = null;
350
+ const walk = (dir) => {
351
+ if (dirContainsExe(dir)) {
352
+ if (path4.basename(dir) === "publish") return dir;
353
+ fallback ??= dir;
354
+ }
355
+ for (const e of fs3.readdirSync(dir, { withFileTypes: true })) {
356
+ if (e.isDirectory()) {
357
+ const found = walk(path4.join(dir, e.name));
358
+ if (found) return found;
359
+ }
360
+ }
361
+ return null;
362
+ };
363
+ return walk(root) ?? fallback;
364
+ };
335
365
 
336
366
  // src/doctor.ts
337
- import { execFileSync as execFileSync2 } from "child_process";
367
+ import { execFileSync as execFileSync3 } from "child_process";
338
368
  import prompts from "prompts";
339
369
  var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
340
370
  var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
341
371
  var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
342
372
  var run = (cmd, args) => {
343
373
  try {
344
- const stdout = execFileSync2(cmd, args, {
374
+ const stdout = execFileSync3(cmd, args, {
345
375
  encoding: "utf-8",
346
376
  stdio: ["ignore", "pipe", "pipe"]
347
377
  });
@@ -497,7 +527,7 @@ var runDoctor = async () => {
497
527
  console.log(
498
528
  footer(
499
529
  `${dim("all checks passed \u2014 you're ready to run")} ${lime(
500
- "vidra dev"
530
+ "npm run dev"
501
531
  )}${dim(".")}`
502
532
  )
503
533
  );
@@ -509,7 +539,7 @@ var runDoctor = async () => {
509
539
  footer(
510
540
  `${dim(
511
541
  `${n} issue${n === 1 ? "" : "s"} found. apply the ${n === 1 ? "fix" : "fixes"} above, then re-run`
512
- )} ${lime("vidra doctor")}${dim(".")}`
542
+ )} ${lime("npm run doctor")}${dim(".")}`
513
543
  )
514
544
  );
515
545
  console.log();
@@ -531,7 +561,7 @@ var installWorkload = (csprojPath) => {
531
561
  );
532
562
  console.log();
533
563
  try {
534
- execFileSync2(DOTNET, args, { stdio: "inherit" });
564
+ execFileSync3(DOTNET, args, { stdio: "inherit" });
535
565
  return true;
536
566
  } catch {
537
567
  console.error();
@@ -639,8 +669,8 @@ var TARGETS = {
639
669
  macos: macosTarget,
640
670
  windows: windowsTarget
641
671
  };
642
- var packageLabel = (target) => target.name === "macos" ? "package DMG" : "package MSIX";
643
- var artifactName = (project, target) => `${project.projectName}-${project.displayVersion}-${target.name}.${target.name === "macos" ? "dmg" : "msix"}`;
672
+ var packageLabel = (target) => target.name === "macos" ? "package DMG" : "package ZIP";
673
+ var artifactName = (project, target) => `${project.projectName}-${project.displayVersion}-${target.name}.${target.name === "macos" ? "dmg" : "zip"}`;
644
674
  var buildCommand = async (argv) => {
645
675
  const args = parseArgs(["_", "_", ...argv]);
646
676
  const verbose = !!args["verbose"];
@@ -761,9 +791,9 @@ var printBuildPlan = (project, target) => {
761
791
  console.log(
762
792
  row({
763
793
  glyph: "active",
764
- label: "package MSIX",
794
+ label: "package ZIP",
765
795
  labelWidth: STEP_LABEL_WIDTH,
766
- detail: `${dim("\u2192")} ${value(artifactName(project, target))}`
796
+ detail: `${dim("self-contained \u2192")} ${value(artifactName(project, target))}`
767
797
  })
768
798
  );
769
799
  }
@@ -912,7 +942,7 @@ var stepPackage = async (project, target, bundlePath) => {
912
942
  // src/commands/dev.ts
913
943
  import path6 from "path";
914
944
  import fs5 from "fs-extra";
915
- import { execFileSync as execFileSync3, spawn } from "child_process";
945
+ import { execFileSync as execFileSync4, spawn } from "child_process";
916
946
  import { request } from "http";
917
947
  var POLL_INTERVAL_MS = 500;
918
948
  var POLL_TIMEOUT_MS = 3e4;
@@ -1037,11 +1067,16 @@ var DevSession = class {
1037
1067
  console.log(taggedRow("active", "ui", dim("starting dev server\u2026")));
1038
1068
  const vite = spawn(NPM_COMMAND, ["run", "dev"], {
1039
1069
  cwd: this.project.uiDir,
1040
- stdio: ["ignore", "pipe", "pipe"]
1070
+ stdio: ["ignore", "pipe", "pipe"],
1071
+ shell: process.platform === "win32"
1041
1072
  });
1042
1073
  return this.registerChild(vite, "ui", "Vite dev server");
1043
1074
  }
1044
- launchMacosHost() {
1075
+ // Builds the MAUI host as a discrete step (a plain `dotnet build`, never
1076
+ // MSBuild's `-t:Run`) so the per-OS launch paths can spawn the produced
1077
+ // binary directly. `-t:Run` shells out in ways that break for both locally
1078
+ // signed mac apps and unpackaged Windows apps (see the call sites).
1079
+ buildHostSync() {
1045
1080
  console.log(
1046
1081
  taggedRow(
1047
1082
  "active",
@@ -1050,7 +1085,7 @@ var DevSession = class {
1050
1085
  )
1051
1086
  );
1052
1087
  try {
1053
- execFileSync3(
1088
+ execFileSync4(
1054
1089
  DOTNET_COMMAND,
1055
1090
  [
1056
1091
  "build",
@@ -1076,6 +1111,9 @@ var DevSession = class {
1076
1111
  }
1077
1112
  process.exit(1);
1078
1113
  }
1114
+ }
1115
+ launchMacosHost() {
1116
+ this.buildHostSync();
1079
1117
  const appBundle = findMacAppBundle(
1080
1118
  this.project.hostDir,
1081
1119
  this.target.framework,
@@ -1117,26 +1155,42 @@ var DevSession = class {
1117
1155
  });
1118
1156
  return this.registerChild(host, "host", path6.basename(binary));
1119
1157
  }
1158
+ // Build first, then spawn the produced .exe directly. A single
1159
+ // `dotnet build -t:Run` on an unpackaged MAUI Windows app
1160
+ // (`WindowsPackageType=None`) execs the binary before the WindowsAppSDK
1161
+ // native assets are laid out beside it, so the app can't resolve its deps and
1162
+ // dies with a bare `MSB3073 ... exited with code 3` (ERROR_PATH_NOT_FOUND —
1163
+ // "The system cannot find the path specified"). Building as a discrete step
1164
+ // and then launching the binary is the documented workaround.
1165
+ // See dotnet/maui#13942 and dotnet/maui#5975.
1120
1166
  launchWindowsHost() {
1121
- console.log(taggedRow("active", "host", dim("launching\u2026")));
1122
- const host = spawn(
1123
- DOTNET_COMMAND,
1124
- [
1125
- "build",
1126
- "-t:Run",
1127
- "-c",
1128
- this.buildConfig,
1129
- "-f",
1130
- this.target.framework,
1131
- this.project.csprojPath
1132
- ],
1133
- {
1134
- cwd: this.project.root,
1135
- stdio: ["ignore", "pipe", "pipe"],
1136
- env: { ...process.env, VIDRA_DEV_URL: this.viteUrl }
1137
- }
1167
+ this.buildHostSync();
1168
+ const exe = findWindowsExecutable(
1169
+ this.project.hostDir,
1170
+ this.project.csprojPath,
1171
+ this.target.framework,
1172
+ this.buildConfig
1173
+ );
1174
+ if (!exe) {
1175
+ console.error(
1176
+ row({
1177
+ glyph: "error",
1178
+ detail: dim(
1179
+ `could not find the host .exe under ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
1180
+ )
1181
+ })
1182
+ );
1183
+ process.exit(1);
1184
+ }
1185
+ console.log(
1186
+ taggedRow("done", "host", `${dim("launched")} ${value(path6.basename(exe))}`)
1138
1187
  );
1139
- return this.registerChild(host, "host", "MAUI host");
1188
+ const host = spawn(exe, [], {
1189
+ cwd: path6.dirname(exe),
1190
+ stdio: ["ignore", "pipe", "pipe"],
1191
+ env: { ...process.env, VIDRA_DEV_URL: this.viteUrl }
1192
+ });
1193
+ return this.registerChild(host, "host", path6.basename(exe));
1140
1194
  }
1141
1195
  registerChild(child, tag2, label) {
1142
1196
  this.children.push(child);
@@ -1283,11 +1337,36 @@ var findMacExecutable = (appBundle) => {
1283
1337
  }
1284
1338
  return null;
1285
1339
  };
1340
+ var findWindowsExecutable = (hostDir, csprojPath, framework, buildConfig) => {
1341
+ const outputDir = path6.join(hostDir, "bin", buildConfig, framework);
1342
+ if (!fs5.existsSync(outputDir)) return null;
1343
+ const exeName = `${path6.basename(csprojPath, ".csproj")}.exe`.toLowerCase();
1344
+ return findFileRecursive(outputDir, (name) => name.toLowerCase() === exeName) ?? findFileRecursive(
1345
+ outputDir,
1346
+ (name) => name.toLowerCase().endsWith(".host.exe")
1347
+ ) ?? findFileRecursive(outputDir, (name) => name.toLowerCase().endsWith(".exe"));
1348
+ };
1349
+ var findFileRecursive = (dir, match) => {
1350
+ if (!fs5.existsSync(dir)) return null;
1351
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
1352
+ for (const entry of entries) {
1353
+ if (entry.isFile() && match(entry.name)) {
1354
+ return path6.join(dir, entry.name);
1355
+ }
1356
+ }
1357
+ for (const entry of entries) {
1358
+ if (entry.isDirectory()) {
1359
+ const found = findFileRecursive(path6.join(dir, entry.name), match);
1360
+ if (found) return found;
1361
+ }
1362
+ }
1363
+ return null;
1364
+ };
1286
1365
  var killChild = (child) => {
1287
1366
  if (!child.pid || child.exitCode !== null) return;
1288
1367
  if (process.platform === "win32") {
1289
1368
  try {
1290
- execFileSync3("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
1369
+ execFileSync4("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
1291
1370
  stdio: "ignore"
1292
1371
  });
1293
1372
  } catch {
package/dist/index.js CHANGED
@@ -468,7 +468,7 @@ var main = async () => {
468
468
  );
469
469
  if (!prereqsReady) {
470
470
  console.log(
471
- ` ${dim("tip: run")} ${lime("vidra doctor")} ${dim("to verify your setup first")}`
471
+ ` ${dim("tip: run")} ${lime("npm run doctor")} ${dim("to verify your setup first")}`
472
472
  );
473
473
  }
474
474
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vidra-app",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Scaffold a new Vidra application (React + .NET MAUI)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,12 +14,16 @@ A cross-platform application built with [Vidra](https://github.com/user/vidra)
14
14
  Not sure if you're set up? Run:
15
15
 
16
16
  ```bash
17
- vidra doctor
17
+ npm run doctor
18
18
  ```
19
19
 
20
20
  It checks your .NET SDK, the MAUI workload, and (on macOS) Xcode, and prints the
21
21
  exact command to fix anything that's missing.
22
22
 
23
+ > The `vidra` CLI ships as a local dev dependency of this project, so run it
24
+ > through npm (`npm run dev`, `npm run doctor`) or with `npx vidra <command>` —
25
+ > there is no global `vidra` command to install.
26
+
23
27
  ### Development
24
28
 
25
29
  ```bash
@@ -31,8 +35,8 @@ This starts the Vite dev server and launches the native host for the current OS.
31
35
  To target a specific desktop platform explicitly:
32
36
 
33
37
  ```bash
34
- vidra dev --target macos
35
- vidra dev --target windows
38
+ npx vidra dev --target macos
39
+ npx vidra dev --target windows
36
40
  ```
37
41
 
38
42
  If you want to run the pieces separately:
@@ -6,7 +6,8 @@
6
6
  "dev": "vidra dev",
7
7
  "dev:ui": "npm run dev --prefix ui",
8
8
  "dev:host:macos": "vidra run --target macos",
9
- "dev:host:windows": "vidra run --target windows"
9
+ "dev:host:windows": "vidra run --target windows",
10
+ "doctor": "vidra doctor"
10
11
  },
11
12
  "devDependencies": {
12
13
  "create-vidra-app": "{{cliVersion}}"