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 +9 -6
- package/dist/cli.js +129 -50
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/react-vite/README.md +7 -3
- package/templates/react-vite/package.json +2 -1
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
vidra
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
|
314
|
-
if (
|
|
315
|
-
|
|
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(
|
|
329
|
-
const outName = `${meta.projectName}-${meta.displayVersion}-windows.
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
-
"
|
|
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("
|
|
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
|
-
|
|
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
|
|
643
|
-
var artifactName = (project, target) => `${project.projectName}-${project.displayVersion}-${target.name}.${target.name === "macos" ? "dmg" : "
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
@@ -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
|
-
|
|
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}}"
|