@thelacanians/vue-native-cli 0.4.14 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,29 +3,271 @@
3
3
  // src/cli.ts
4
4
  import { program } from "commander";
5
5
 
6
- // src/commands/create.ts
6
+ // src/commands/build.ts
7
7
  import { Command } from "commander";
8
+ import { spawn, execSync } from "child_process";
9
+ import { existsSync, readdirSync, mkdirSync, copyFileSync } from "fs";
10
+ import { join, basename } from "path";
11
+ import pc from "picocolors";
12
+ function findXcodeProject(iosDir) {
13
+ if (!existsSync(iosDir)) return null;
14
+ for (const ext of [".xcworkspace", ".xcodeproj"]) {
15
+ try {
16
+ const entries = readdirSync(iosDir);
17
+ const match = entries.find((e) => e.endsWith(ext));
18
+ if (match) {
19
+ return {
20
+ path: join(iosDir, match),
21
+ isWorkspace: ext === ".xcworkspace"
22
+ };
23
+ }
24
+ } catch {
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ function findReleaseApk(androidDir) {
30
+ const apkDir = join(androidDir, "app", "build", "outputs", "apk", "release");
31
+ if (existsSync(apkDir)) {
32
+ try {
33
+ const entries = readdirSync(apkDir);
34
+ const apk = entries.find((e) => e.endsWith(".apk") && !e.includes("androidTest"));
35
+ if (apk) {
36
+ return join(apkDir, apk);
37
+ }
38
+ } catch {
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ function findReleaseAab(androidDir) {
44
+ const aabDir = join(androidDir, "app", "build", "outputs", "bundle", "release");
45
+ if (existsSync(aabDir)) {
46
+ try {
47
+ const entries = readdirSync(aabDir);
48
+ const aab = entries.find((e) => e.endsWith(".aab"));
49
+ if (aab) {
50
+ return join(aabDir, aab);
51
+ }
52
+ } catch {
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ function ensureOutputDir(outputPath) {
58
+ if (!existsSync(outputPath)) {
59
+ mkdirSync(outputPath, { recursive: true });
60
+ }
61
+ }
62
+ var buildCommand = new Command("build").description("Create a release build of the app").argument("<platform>", "platform to build for (ios, android)").option("--mode <mode>", "build mode", "release").option("--output <path>", "output directory for the build artifact", "./build").option("--scheme <scheme>", "Xcode scheme to build (iOS only)").option("--aab", "build Android App Bundle (.aab) instead of APK").action(async (platform, options) => {
63
+ if (platform !== "ios" && platform !== "android") {
64
+ console.error(pc.red('Platform must be "ios" or "android"'));
65
+ process.exit(1);
66
+ }
67
+ const cwd = process.cwd();
68
+ const outputDir = join(cwd, options.output);
69
+ console.log(pc.cyan(`
70
+ Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${platform === "ios" ? "iOS" : "Android"})
71
+ `));
72
+ console.log(pc.white(" Building JS bundle for production..."));
73
+ try {
74
+ execSync("bun run vite build --mode production", { cwd, stdio: "inherit" });
75
+ console.log(pc.green(" \u2713 Bundle built\n"));
76
+ } catch {
77
+ console.error(pc.red(" \u2717 Bundle build failed"));
78
+ process.exit(1);
79
+ }
80
+ if (platform === "ios") {
81
+ buildIOS(cwd, outputDir, options);
82
+ } else {
83
+ buildAndroid(cwd, outputDir, options);
84
+ }
85
+ });
86
+ function buildIOS(cwd, outputDir, options) {
87
+ const iosDir = join(cwd, "ios");
88
+ const project = findXcodeProject(iosDir);
89
+ if (!project) {
90
+ console.log(pc.yellow(" No Xcode project found in ./ios/"));
91
+ console.log(pc.dim(" To add iOS support, create an Xcode project in the ios/ directory."));
92
+ console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
93
+ return;
94
+ }
95
+ const projectFlag = project.isWorkspace ? "-workspace" : "-project";
96
+ const scheme = options.scheme || project.path.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
97
+ const configuration = options.mode === "release" ? "Release" : "Debug";
98
+ const archivePath = join(outputDir, `${scheme}.xcarchive`);
99
+ ensureOutputDir(outputDir);
100
+ console.log(pc.white(` Archiving ${scheme} (${configuration})...`));
101
+ console.log(pc.dim(` Archive path: ${archivePath}`));
102
+ const xcodebuild = spawn(
103
+ "xcodebuild",
104
+ [
105
+ projectFlag,
106
+ project.path,
107
+ "-scheme",
108
+ scheme,
109
+ "-configuration",
110
+ configuration,
111
+ "-destination",
112
+ "generic/platform=iOS",
113
+ "-archivePath",
114
+ archivePath,
115
+ "archive"
116
+ ],
117
+ {
118
+ cwd,
119
+ stdio: "pipe",
120
+ env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
121
+ }
122
+ );
123
+ const cleanup = () => {
124
+ if (xcodebuild && !xcodebuild.killed) {
125
+ xcodebuild.kill();
126
+ }
127
+ };
128
+ process.on("exit", cleanup);
129
+ process.on("SIGINT", cleanup);
130
+ process.on("SIGTERM", cleanup);
131
+ xcodebuild.stdout?.on("data", (data) => {
132
+ const text = data.toString().trim();
133
+ if (text.includes("Compiling") || text.includes("Linking") || text.includes("Signing")) {
134
+ console.log(pc.dim(` ${text.split("\n").pop()}`));
135
+ }
136
+ });
137
+ xcodebuild.stderr?.on("data", (data) => {
138
+ const text = data.toString().trim();
139
+ if (text.includes("error:")) {
140
+ console.log(pc.red(` ${text}`));
141
+ } else if (text.includes("warning:")) {
142
+ console.log(pc.yellow(` ${text}`));
143
+ }
144
+ });
145
+ xcodebuild.on("error", (err) => {
146
+ console.error(pc.red(` xcodebuild process error: ${err.message}`));
147
+ cleanup();
148
+ });
149
+ xcodebuild.on("close", (code) => {
150
+ if (code !== 0) {
151
+ console.error(pc.red(` \u2717 Archive failed (exit code ${code})`));
152
+ process.exit(1);
153
+ }
154
+ console.log(pc.green(" \u2713 Archive successful\n"));
155
+ if (existsSync(archivePath)) {
156
+ console.log(pc.green(` Archive: ${archivePath}`));
157
+ console.log(pc.dim(" To export an IPA, open the archive in Xcode Organizer or run:"));
158
+ console.log(pc.dim(` xcodebuild -exportArchive -archivePath "${archivePath}" -exportOptionsPlist ExportOptions.plist -exportPath "${outputDir}"
159
+ `));
160
+ } else {
161
+ console.log(pc.yellow(" Archive path not found. Check Xcode build settings.\n"));
162
+ }
163
+ });
164
+ }
165
+ function buildAndroid(cwd, outputDir, options) {
166
+ const androidDir = join(cwd, "android");
167
+ if (!existsSync(androidDir)) {
168
+ console.log(pc.yellow(" No android/ directory found."));
169
+ console.log(pc.dim(" To add Android support, create an Android project in the android/ directory."));
170
+ console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
171
+ return;
172
+ }
173
+ const gradlew = join(androidDir, "gradlew");
174
+ if (!existsSync(gradlew)) {
175
+ console.error(pc.red(" \u2717 gradlew not found in android/ directory"));
176
+ console.log(pc.dim(" Make sure your Android project has the Gradle wrapper.\n"));
177
+ process.exit(1);
178
+ }
179
+ const gradleTask = options.aab ? "bundleRelease" : "assembleRelease";
180
+ const artifactType = options.aab ? "AAB" : "APK";
181
+ ensureOutputDir(outputDir);
182
+ console.log(pc.white(` Building release ${artifactType} with Gradle...`));
183
+ const gradle = spawn(
184
+ "./gradlew",
185
+ [gradleTask],
186
+ {
187
+ cwd: androidDir,
188
+ stdio: "pipe",
189
+ env: { ...process.env }
190
+ }
191
+ );
192
+ const cleanupGradle = () => {
193
+ if (gradle && !gradle.killed) {
194
+ gradle.kill();
195
+ }
196
+ };
197
+ process.on("exit", cleanupGradle);
198
+ process.on("SIGINT", cleanupGradle);
199
+ process.on("SIGTERM", cleanupGradle);
200
+ gradle.stdout?.on("data", (data) => {
201
+ const text = data.toString().trim();
202
+ if (text) {
203
+ console.log(pc.dim(` ${text}`));
204
+ }
205
+ });
206
+ gradle.stderr?.on("data", (data) => {
207
+ const text = data.toString().trim();
208
+ if (text.includes("ERROR") || text.includes("FAILURE")) {
209
+ console.log(pc.red(` ${text}`));
210
+ }
211
+ });
212
+ gradle.on("error", (err) => {
213
+ console.error(pc.red(` Gradle process error: ${err.message}`));
214
+ cleanupGradle();
215
+ });
216
+ gradle.on("close", (code) => {
217
+ if (code !== 0) {
218
+ console.error(pc.red(` \u2717 Gradle build failed (exit code ${code})`));
219
+ process.exit(1);
220
+ }
221
+ console.log(pc.green(" \u2713 Build successful\n"));
222
+ if (options.aab) {
223
+ const aabPath = findReleaseAab(androidDir);
224
+ if (aabPath) {
225
+ const destPath = join(outputDir, basename(aabPath));
226
+ copyFileSync(aabPath, destPath);
227
+ console.log(pc.green(` AAB copied to: ${destPath}`));
228
+ console.log(pc.dim(" Upload this file to the Google Play Console.\n"));
229
+ } else {
230
+ console.log(pc.yellow(" Could not locate release AAB."));
231
+ console.log(pc.dim(" Expected at android/app/build/outputs/bundle/release/\n"));
232
+ }
233
+ } else {
234
+ const apkPath = findReleaseApk(androidDir);
235
+ if (apkPath) {
236
+ const destPath = join(outputDir, basename(apkPath));
237
+ copyFileSync(apkPath, destPath);
238
+ console.log(pc.green(` APK copied to: ${destPath}`));
239
+ console.log(pc.dim(' Install with: adb install -r "' + destPath + '"\n'));
240
+ } else {
241
+ console.log(pc.yellow(" Could not locate release APK."));
242
+ console.log(pc.dim(" Expected at android/app/build/outputs/apk/release/\n"));
243
+ }
244
+ }
245
+ });
246
+ }
247
+
248
+ // src/commands/create.ts
249
+ import { Command as Command2 } from "commander";
8
250
  import { cp, mkdir, writeFile } from "fs/promises";
9
- import { join, dirname } from "path";
251
+ import { join as join2, dirname } from "path";
10
252
  import { fileURLToPath } from "url";
11
- import { existsSync } from "fs";
12
- import pc from "picocolors";
253
+ import { existsSync as existsSync2 } from "fs";
254
+ import pc2 from "picocolors";
13
255
  var VERSION = "0.4.14";
14
- var createCommand = new Command("create").description("Create a new Vue Native project").argument("<name>", "project name").option("-t, --template <template>", "project template (blank, tabs, drawer)", "blank").action(async (name, options) => {
256
+ var createCommand = new Command2("create").description("Create a new Vue Native project").argument("<name>", "project name").option("-t, --template <template>", "project template (blank, tabs, drawer)", "blank").action(async (name, options) => {
15
257
  const template = options.template;
16
258
  if (!["blank", "tabs", "drawer"].includes(template)) {
17
- console.error(pc.red(`Invalid template "${template}". Choose: blank, tabs, drawer`));
259
+ console.error(pc2.red(`Invalid template "${template}". Choose: blank, tabs, drawer`));
18
260
  process.exit(1);
19
261
  }
20
- const dir = join(process.cwd(), name);
21
- console.log(pc.cyan(`
22
- Creating Vue Native project: ${pc.bold(name)} (template: ${template})
262
+ const dir = join2(process.cwd(), name);
263
+ console.log(pc2.cyan(`
264
+ Creating Vue Native project: ${pc2.bold(name)} (template: ${template})
23
265
  `));
24
266
  try {
25
267
  await mkdir(dir, { recursive: true });
26
- await mkdir(join(dir, "app"), { recursive: true });
27
- await mkdir(join(dir, "app", "pages"), { recursive: true });
28
- await writeFile(join(dir, "package.json"), JSON.stringify({
268
+ await mkdir(join2(dir, "app"), { recursive: true });
269
+ await mkdir(join2(dir, "app", "pages"), { recursive: true });
270
+ await writeFile(join2(dir, "package.json"), JSON.stringify({
29
271
  name,
30
272
  version: "0.0.1",
31
273
  private: true,
@@ -47,7 +289,7 @@ Creating Vue Native project: ${pc.bold(name)} (template: ${template})
47
289
  "typescript": "^5.7.0"
48
290
  }
49
291
  }, null, 2));
50
- await writeFile(join(dir, "vite.config.ts"), `import { defineConfig } from 'vite'
292
+ await writeFile(join2(dir, "vite.config.ts"), `import { defineConfig } from 'vite'
51
293
  import vue from '@vitejs/plugin-vue'
52
294
  import vueNative from '@thelacanians/vue-native-vite-plugin'
53
295
 
@@ -55,7 +297,7 @@ export default defineConfig({
55
297
  plugins: [vue(), vueNative()],
56
298
  })
57
299
  `);
58
- await writeFile(join(dir, "tsconfig.json"), JSON.stringify({
300
+ await writeFile(join2(dir, "tsconfig.json"), JSON.stringify({
59
301
  compilerOptions: {
60
302
  target: "ES2020",
61
303
  module: "ESNext",
@@ -71,12 +313,12 @@ export default defineConfig({
71
313
  include: ["app/**/*", "env.d.ts"]
72
314
  }, null, 2));
73
315
  await generateTemplateFiles(dir, name, template);
74
- const iosDir = join(dir, "ios");
75
- const iosSrcDir = join(iosDir, "Sources");
316
+ const iosDir = join2(dir, "ios");
317
+ const iosSrcDir = join2(iosDir, "Sources");
76
318
  await mkdir(iosSrcDir, { recursive: true });
77
319
  const xcodeProjectName = name.replace(/[^a-zA-Z0-9]/g, "");
78
320
  const bundleId = `com.vuenative.${name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()}`;
79
- await writeFile(join(iosDir, "project.yml"), `name: ${xcodeProjectName}
321
+ await writeFile(join2(iosDir, "project.yml"), `name: ${xcodeProjectName}
80
322
  options:
81
323
  bundleIdPrefix: com.vuenative
82
324
  deploymentTarget:
@@ -107,7 +349,7 @@ targets:
107
349
  - path: ../dist/vue-native-bundle.js
108
350
  optional: true
109
351
  `);
110
- await writeFile(join(iosSrcDir, "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
352
+ await writeFile(join2(iosSrcDir, "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
111
353
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
112
354
  <plist version="1.0">
113
355
  <dict>
@@ -173,7 +415,7 @@ targets:
173
415
  </dict>
174
416
  </plist>
175
417
  `);
176
- await writeFile(join(iosSrcDir, "AppDelegate.swift"), `import UIKit
418
+ await writeFile(join2(iosSrcDir, "AppDelegate.swift"), `import UIKit
177
419
 
178
420
  @main
179
421
  class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -197,7 +439,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
197
439
  }
198
440
  }
199
441
  `);
200
- await writeFile(join(iosSrcDir, "SceneDelegate.swift"), `import UIKit
442
+ await writeFile(join2(iosSrcDir, "SceneDelegate.swift"), `import UIKit
201
443
  import VueNativeCore
202
444
 
203
445
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -225,29 +467,29 @@ class AppViewController: VueNativeViewController {
225
467
  #endif
226
468
  }
227
469
  `);
228
- const androidDir = join(dir, "android");
229
- const androidAppDir = join(androidDir, "app");
470
+ const androidDir = join2(dir, "android");
471
+ const androidAppDir = join2(androidDir, "app");
230
472
  const androidPkg = `com.vuenative.${name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()}`;
231
473
  const androidPkgPath = androidPkg.replace(/\./g, "/");
232
- const androidSrcDir = join(androidAppDir, "src", "main");
233
- const androidKotlinDir = join(androidSrcDir, "kotlin", androidPkgPath);
234
- const androidResValuesDir = join(androidSrcDir, "res", "values");
235
- const androidResXmlDir = join(androidSrcDir, "res", "xml");
236
- const androidDebugResXmlDir = join(androidAppDir, "src", "debug", "res", "xml");
237
- const androidGradleWrapperDir = join(androidDir, "gradle", "wrapper");
474
+ const androidSrcDir = join2(androidAppDir, "src", "main");
475
+ const androidKotlinDir = join2(androidSrcDir, "kotlin", androidPkgPath);
476
+ const androidResValuesDir = join2(androidSrcDir, "res", "values");
477
+ const androidResXmlDir = join2(androidSrcDir, "res", "xml");
478
+ const androidDebugResXmlDir = join2(androidAppDir, "src", "debug", "res", "xml");
479
+ const androidGradleWrapperDir = join2(androidDir, "gradle", "wrapper");
238
480
  await mkdir(androidKotlinDir, { recursive: true });
239
481
  await mkdir(androidResValuesDir, { recursive: true });
240
482
  await mkdir(androidResXmlDir, { recursive: true });
241
483
  await mkdir(androidDebugResXmlDir, { recursive: true });
242
484
  await mkdir(androidGradleWrapperDir, { recursive: true });
243
- await writeFile(join(androidDir, "build.gradle.kts"), `// Top-level build file
485
+ await writeFile(join2(androidDir, "build.gradle.kts"), `// Top-level build file
244
486
  plugins {
245
487
  id("com.android.application") version "8.7.3" apply false
246
488
  id("com.android.library") version "8.7.3" apply false
247
489
  id("org.jetbrains.kotlin.android") version "2.0.21" apply false
248
490
  }
249
491
  `);
250
- await writeFile(join(androidDir, "settings.gradle.kts"), `pluginManagement {
492
+ await writeFile(join2(androidDir, "settings.gradle.kts"), `pluginManagement {
251
493
  repositories {
252
494
  google()
253
495
  mavenCentral()
@@ -273,7 +515,7 @@ dependencyResolutionManagement {
273
515
  rootProject.name = "${name}"
274
516
  include(":app")
275
517
  `);
276
- await writeFile(join(androidAppDir, "build.gradle.kts"), `plugins {
518
+ await writeFile(join2(androidAppDir, "build.gradle.kts"), `plugins {
277
519
  id("com.android.application")
278
520
  id("org.jetbrains.kotlin.android")
279
521
  }
@@ -321,13 +563,13 @@ dependencies {
321
563
  implementation("androidx.core:core-ktx:1.15.0")
322
564
  }
323
565
  `);
324
- await writeFile(join(androidAppDir, "proguard-rules.pro"), `# Vue Native
566
+ await writeFile(join2(androidAppDir, "proguard-rules.pro"), `# Vue Native
325
567
  -keep class com.vuenative.** { *; }
326
568
 
327
569
  # J2V8
328
570
  -keep class com.eclipsesource.v8.** { *; }
329
571
  `);
330
- await writeFile(join(androidSrcDir, "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
572
+ await writeFile(join2(androidSrcDir, "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
331
573
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
332
574
  <uses-permission android:name="android.permission.INTERNET" />
333
575
 
@@ -350,12 +592,12 @@ dependencies {
350
592
  </application>
351
593
  </manifest>
352
594
  `);
353
- await writeFile(join(androidResValuesDir, "strings.xml"), `<?xml version="1.0" encoding="utf-8"?>
595
+ await writeFile(join2(androidResValuesDir, "strings.xml"), `<?xml version="1.0" encoding="utf-8"?>
354
596
  <resources>
355
597
  <string name="app_name">${name}</string>
356
598
  </resources>
357
599
  `);
358
- await writeFile(join(androidResValuesDir, "themes.xml"), `<?xml version="1.0" encoding="utf-8"?>
600
+ await writeFile(join2(androidResValuesDir, "themes.xml"), `<?xml version="1.0" encoding="utf-8"?>
359
601
  <resources>
360
602
  <style name="Theme.VueNative" parent="Theme.MaterialComponents.Light.NoActionBar">
361
603
  <item name="colorPrimary">#4F46E5</item>
@@ -368,12 +610,12 @@ dependencies {
368
610
  </style>
369
611
  </resources>
370
612
  `);
371
- await writeFile(join(androidResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
613
+ await writeFile(join2(androidResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
372
614
  <network-security-config>
373
615
  <base-config cleartextTrafficPermitted="false" />
374
616
  </network-security-config>
375
617
  `);
376
- await writeFile(join(androidDebugResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
618
+ await writeFile(join2(androidDebugResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
377
619
  <network-security-config>
378
620
  <domain-config cleartextTrafficPermitted="true">
379
621
  <domain includeSubdomains="true">localhost</domain>
@@ -382,7 +624,7 @@ dependencies {
382
624
  </domain-config>
383
625
  </network-security-config>
384
626
  `);
385
- await writeFile(join(androidKotlinDir, "MainActivity.kt"), `package ${androidPkg}
627
+ await writeFile(join2(androidKotlinDir, "MainActivity.kt"), `package ${androidPkg}
386
628
 
387
629
  import com.vuenative.core.VueNativeActivity
388
630
 
@@ -396,20 +638,20 @@ class MainActivity : VueNativeActivity() {
396
638
  }
397
639
  }
398
640
  `);
399
- await writeFile(join(androidDir, "gradle.properties"), `# Project-wide Gradle settings
641
+ await writeFile(join2(androidDir, "gradle.properties"), `# Project-wide Gradle settings
400
642
  org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
401
643
  android.useAndroidX=true
402
644
  kotlin.code.style=official
403
645
  android.nonTransitiveRClass=true
404
646
  `);
405
- await writeFile(join(androidGradleWrapperDir, "gradle-wrapper.properties"), `distributionBase=GRADLE_USER_HOME
647
+ await writeFile(join2(androidGradleWrapperDir, "gradle-wrapper.properties"), `distributionBase=GRADLE_USER_HOME
406
648
  distributionPath=wrapper/dists
407
649
  distributionUrl=https\\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
408
650
  networkTimeout=10000
409
651
  zipStoreBase=GRADLE_USER_HOME
410
652
  zipStorePath=wrapper/dists
411
653
  `);
412
- await writeFile(join(dir, "vue-native.config.ts"), `import { defineConfig } from '@thelacanians/vue-native-cli'
654
+ await writeFile(join2(dir, "vue-native.config.ts"), `import { defineConfig } from '@thelacanians/vue-native-cli'
413
655
 
414
656
  export default defineConfig({
415
657
  name: '${name}',
@@ -424,7 +666,7 @@ export default defineConfig({
424
666
  },
425
667
  })
426
668
  `);
427
- await writeFile(join(dir, "env.d.ts"), `/// <reference types="vite/client" />
669
+ await writeFile(join2(dir, "env.d.ts"), `/// <reference types="vite/client" />
428
670
  declare module '*.vue' {
429
671
  import type { DefineComponent } from '@thelacanians/vue-native-runtime'
430
672
  const component: DefineComponent<{}, {}, any>
@@ -432,7 +674,7 @@ declare module '*.vue' {
432
674
  }
433
675
  declare const __DEV__: boolean
434
676
  `);
435
- await writeFile(join(dir, ".gitignore"), `node_modules/
677
+ await writeFile(join2(dir, ".gitignore"), `node_modules/
436
678
  dist/
437
679
  *.xcuserstate
438
680
  *.xcuserdatad/
@@ -455,30 +697,30 @@ local.properties
455
697
  *.jks
456
698
  `);
457
699
  const cliDir = dirname(dirname(fileURLToPath(import.meta.url)));
458
- const bundledNative = join(cliDir, "native");
459
- if (existsSync(bundledNative)) {
460
- const nativeDir = join(dir, "native");
700
+ const bundledNative = join2(cliDir, "native");
701
+ if (existsSync2(bundledNative)) {
702
+ const nativeDir = join2(dir, "native");
461
703
  await cp(bundledNative, nativeDir, { recursive: true });
462
- console.log(pc.dim(" Bundled native/ copied as fallback.\n"));
704
+ console.log(pc2.dim(" Bundled native/ copied as fallback.\n"));
463
705
  }
464
- console.log(pc.green(" Project created successfully!\n"));
465
- console.log(pc.white(" Next steps:\n"));
466
- console.log(pc.white(` cd ${name}`));
467
- console.log(pc.white(" bun install"));
468
- console.log(pc.white(" vue-native dev\n"));
469
- console.log(pc.white(" To run on iOS:"));
470
- console.log(pc.white(" vue-native run ios\n"));
471
- console.log(pc.white(" To run on Android:"));
472
- console.log(pc.dim(" Open android/ in Android Studio, or run:"));
473
- console.log(pc.dim(" cd android && gradle wrapper && cd .."));
474
- console.log(pc.white(" vue-native run android\n"));
706
+ console.log(pc2.green(" Project created successfully!\n"));
707
+ console.log(pc2.white(" Next steps:\n"));
708
+ console.log(pc2.white(` cd ${name}`));
709
+ console.log(pc2.white(" bun install"));
710
+ console.log(pc2.white(" vue-native dev\n"));
711
+ console.log(pc2.white(" To run on iOS:"));
712
+ console.log(pc2.white(" vue-native run ios\n"));
713
+ console.log(pc2.white(" To run on Android:"));
714
+ console.log(pc2.dim(" Open android/ in Android Studio, or run:"));
715
+ console.log(pc2.dim(" cd android && gradle wrapper && cd .."));
716
+ console.log(pc2.white(" vue-native run android\n"));
475
717
  } catch (err) {
476
- console.error(pc.red(`Error creating project: ${err.message}`));
718
+ console.error(pc2.red(`Error creating project: ${err.message}`));
477
719
  process.exit(1);
478
720
  }
479
721
  });
480
722
  async function generateTemplateFiles(dir, name, template) {
481
- const pagesDir = join(dir, "app", "pages");
723
+ const pagesDir = join2(dir, "app", "pages");
482
724
  if (template === "blank") {
483
725
  await generateBlankTemplate(dir, pagesDir);
484
726
  } else if (template === "tabs") {
@@ -488,7 +730,7 @@ async function generateTemplateFiles(dir, name, template) {
488
730
  }
489
731
  }
490
732
  async function generateBlankTemplate(dir, pagesDir) {
491
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
733
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
492
734
  import { createRouter } from '@thelacanians/vue-native-navigation'
493
735
  import App from './App.vue'
494
736
  import Home from './pages/Home.vue'
@@ -501,7 +743,7 @@ const app = createApp(App)
501
743
  app.use(router)
502
744
  app.start()
503
745
  `);
504
- await writeFile(join(dir, "app", "App.vue"), `<template>
746
+ await writeFile(join2(dir, "app", "App.vue"), `<template>
505
747
  <VSafeArea :style="{ flex: 1, backgroundColor: '#ffffff' }">
506
748
  <RouterView />
507
749
  </VSafeArea>
@@ -511,7 +753,7 @@ app.start()
511
753
  import { RouterView } from '@thelacanians/vue-native-navigation'
512
754
  </script>
513
755
  `);
514
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
756
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
515
757
  import { ref } from 'vue'
516
758
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
517
759
 
@@ -563,13 +805,13 @@ const styles = createStyleSheet({
563
805
  `);
564
806
  }
565
807
  async function generateTabsTemplate(dir, pagesDir) {
566
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
808
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
567
809
  import App from './App.vue'
568
810
 
569
811
  const app = createApp(App)
570
812
  app.start()
571
813
  `);
572
- await writeFile(join(dir, "app", "App.vue"), `<script setup lang="ts">
814
+ await writeFile(join2(dir, "app", "App.vue"), `<script setup lang="ts">
573
815
  import { createTabNavigator } from '@thelacanians/vue-native-navigation'
574
816
  import Home from './pages/Home.vue'
575
817
  import Settings from './pages/Settings.vue'
@@ -588,7 +830,7 @@ const { TabNavigator } = createTabNavigator()
588
830
  </VSafeArea>
589
831
  </template>
590
832
  `);
591
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
833
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
592
834
  import { ref } from 'vue'
593
835
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
594
836
 
@@ -632,7 +874,7 @@ const styles = createStyleSheet({
632
874
  </VView>
633
875
  </template>
634
876
  `);
635
- await writeFile(join(pagesDir, "Settings.vue"), `<script setup lang="ts">
877
+ await writeFile(join2(pagesDir, "Settings.vue"), `<script setup lang="ts">
636
878
  import { ref } from 'vue'
637
879
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
638
880
 
@@ -676,13 +918,13 @@ const styles = createStyleSheet({
676
918
  `);
677
919
  }
678
920
  async function generateDrawerTemplate(dir, pagesDir) {
679
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
921
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
680
922
  import App from './App.vue'
681
923
 
682
924
  const app = createApp(App)
683
925
  app.start()
684
926
  `);
685
- await writeFile(join(dir, "app", "App.vue"), `<script setup lang="ts">
927
+ await writeFile(join2(dir, "app", "App.vue"), `<script setup lang="ts">
686
928
  import { createDrawerNavigator } from '@thelacanians/vue-native-navigation'
687
929
  import Home from './pages/Home.vue'
688
930
  import About from './pages/About.vue'
@@ -701,7 +943,7 @@ const { DrawerNavigator } = createDrawerNavigator()
701
943
  </VSafeArea>
702
944
  </template>
703
945
  `);
704
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
946
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
705
947
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
706
948
  import { useDrawer } from '@thelacanians/vue-native-navigation'
707
949
 
@@ -754,7 +996,7 @@ const styles = createStyleSheet({
754
996
  </VView>
755
997
  </template>
756
998
  `);
757
- await writeFile(join(pagesDir, "About.vue"), `<script setup lang="ts">
999
+ await writeFile(join2(pagesDir, "About.vue"), `<script setup lang="ts">
758
1000
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
759
1001
  import { useDrawer } from '@thelacanians/vue-native-navigation'
760
1002
 
@@ -810,19 +1052,19 @@ const styles = createStyleSheet({
810
1052
  }
811
1053
 
812
1054
  // src/commands/dev.ts
813
- import { Command as Command2 } from "commander";
814
- import { spawn, execSync } from "child_process";
1055
+ import { Command as Command3 } from "commander";
1056
+ import { spawn as spawn2, execSync as execSync2 } from "child_process";
815
1057
  import { readFile } from "fs/promises";
816
- import { existsSync as existsSync2 } from "fs";
817
- import { join as join2 } from "path";
1058
+ import { existsSync as existsSync3 } from "fs";
1059
+ import { join as join3 } from "path";
818
1060
  import { watch } from "chokidar";
819
1061
  import { WebSocketServer, WebSocket } from "ws";
820
- import pc2 from "picocolors";
1062
+ import pc3 from "picocolors";
821
1063
  var DEFAULT_PORT = 8174;
822
1064
  var BUNDLE_FILE = "dist/vue-native-bundle.js";
823
1065
  function detectIOSSimulators() {
824
1066
  try {
825
- const output = execSync("xcrun simctl list devices available -j", {
1067
+ const output = execSync2("xcrun simctl list devices available -j", {
826
1068
  stdio: "pipe",
827
1069
  encoding: "utf8"
828
1070
  });
@@ -847,33 +1089,33 @@ function detectIOSSimulators() {
847
1089
  }
848
1090
  function bootSimulator(udid) {
849
1091
  try {
850
- execSync(`xcrun simctl boot "${udid}"`, { stdio: "pipe" });
1092
+ execSync2(`xcrun simctl boot "${udid}"`, { stdio: "pipe" });
851
1093
  } catch {
852
1094
  }
853
1095
  try {
854
- execSync("open -a Simulator", { stdio: "pipe" });
1096
+ execSync2("open -a Simulator", { stdio: "pipe" });
855
1097
  } catch {
856
1098
  }
857
1099
  }
858
1100
  function detectAndroidEmulators() {
859
1101
  try {
860
- const output = execSync("adb devices", { stdio: "pipe", encoding: "utf8" });
1102
+ const output = execSync2("adb devices", { stdio: "pipe", encoding: "utf8" });
861
1103
  const lines = output.split("\n").filter((l) => l.includes("device") && !l.startsWith("List"));
862
1104
  return lines.map((l) => l.split(" ")[0]).filter(Boolean);
863
1105
  } catch {
864
1106
  return [];
865
1107
  }
866
1108
  }
867
- var devCommand = new Command2("dev").description("Start the Vue Native dev server with hot reload").option("-p, --port <port>", "WebSocket port for hot reload", String(DEFAULT_PORT)).option("--ios", "auto-detect and launch iOS Simulator").option("--android", "auto-detect Android emulator").option("--simulator <name>", "specify iOS Simulator name").action(async (options) => {
1109
+ var devCommand = new Command3("dev").description("Start the Vue Native dev server with hot reload").option("-p, --port <port>", "WebSocket port for hot reload", String(DEFAULT_PORT)).option("--ios", "auto-detect and launch iOS Simulator").option("--android", "auto-detect Android emulator").option("--simulator <name>", "specify iOS Simulator name").action(async (options) => {
868
1110
  const port = parseInt(options.port, 10);
869
1111
  const cwd = process.cwd();
870
- const bundlePath = join2(cwd, BUNDLE_FILE);
871
- console.log(pc2.cyan("\n Vue Native Dev Server\n"));
1112
+ const bundlePath = join3(cwd, BUNDLE_FILE);
1113
+ console.log(pc3.cyan("\n Vue Native Dev Server\n"));
872
1114
  if (options.ios) {
873
- console.log(pc2.white(" Detecting iOS Simulators..."));
1115
+ console.log(pc3.white(" Detecting iOS Simulators..."));
874
1116
  const simulators = detectIOSSimulators();
875
1117
  if (simulators.length === 0) {
876
- console.log(pc2.yellow(" No iOS Simulators found. Install Xcode and create a simulator."));
1118
+ console.log(pc3.yellow(" No iOS Simulators found. Install Xcode and create a simulator."));
877
1119
  } else {
878
1120
  let target = simulators.find((s) => s.state === "Booted");
879
1121
  if (!target && options.simulator) {
@@ -884,46 +1126,69 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
884
1126
  }
885
1127
  if (target) {
886
1128
  if (target.state !== "Booted") {
887
- console.log(pc2.white(` Booting ${target.name}...`));
1129
+ console.log(pc3.white(` Booting ${target.name}...`));
888
1130
  bootSimulator(target.udid);
889
1131
  }
890
- console.log(pc2.green(` iOS Simulator ready: ${target.name}`));
1132
+ console.log(pc3.green(` iOS Simulator ready: ${target.name}`));
891
1133
  }
892
1134
  }
893
1135
  console.log();
894
1136
  }
895
1137
  if (options.android) {
896
- console.log(pc2.white(" Detecting Android emulators..."));
1138
+ console.log(pc3.white(" Detecting Android emulators..."));
897
1139
  const emulators = detectAndroidEmulators();
898
1140
  if (emulators.length === 0) {
899
- console.log(pc2.yellow(" No Android emulators detected. Start one via Android Studio or `emulator -avd <name>`."));
1141
+ console.log(pc3.yellow(" No Android emulators detected. Start one via Android Studio or `emulator -avd <name>`."));
900
1142
  } else {
901
- console.log(pc2.green(` Android emulator(s) connected: ${emulators.join(", ")}`));
1143
+ console.log(pc3.green(` Android emulator(s) connected: ${emulators.join(", ")}`));
902
1144
  }
903
1145
  console.log();
904
1146
  }
1147
+ let lanIP = "";
1148
+ try {
1149
+ const nets = await import("os").then((os) => os.networkInterfaces());
1150
+ for (const ifaces of Object.values(nets)) {
1151
+ for (const iface of ifaces ?? []) {
1152
+ if (iface.family === "IPv4" && !iface.internal) {
1153
+ lanIP = iface.address;
1154
+ break;
1155
+ }
1156
+ }
1157
+ if (lanIP) break;
1158
+ }
1159
+ } catch {
1160
+ }
1161
+ const isPrivateOrLocal = (addr) => {
1162
+ if (!addr) return false;
1163
+ const ip = addr.replace(/^::ffff:/, "");
1164
+ if (ip === "127.0.0.1" || ip === "::1") return true;
1165
+ if (ip === "10.0.2.2") return true;
1166
+ if (ip.startsWith("10.")) return true;
1167
+ if (ip.startsWith("192.168.")) return true;
1168
+ if (/^172\.(1[6-9]|2\d|3[01])\./.test(ip)) return true;
1169
+ return false;
1170
+ };
905
1171
  const wss = new WebSocketServer({
906
1172
  port,
907
1173
  verifyClient: (info) => {
908
- const addr = info.req.socket.remoteAddress;
909
- return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
1174
+ return isPrivateOrLocal(info.req.socket.remoteAddress);
910
1175
  }
911
1176
  });
912
1177
  const clients = /* @__PURE__ */ new Set();
913
1178
  wss.on("connection", (ws) => {
914
1179
  clients.add(ws);
915
1180
  ws.send(JSON.stringify({ type: "connected" }));
916
- console.log(pc2.green(` Client connected (${clients.size} total)`));
1181
+ console.log(pc3.green(` Client connected (${clients.size} total)`));
917
1182
  readFile(bundlePath, "utf8").then((bundle) => {
918
1183
  if (ws.readyState === WebSocket.OPEN) {
919
1184
  ws.send(JSON.stringify({ type: "bundle", bundle }));
920
- console.log(pc2.dim(` Sent bundle to new client (${Math.round(bundle.length / 1024)}KB)`));
1185
+ console.log(pc3.dim(` Sent bundle to new client (${Math.round(bundle.length / 1024)}KB)`));
921
1186
  }
922
1187
  }).catch(() => {
923
1188
  });
924
1189
  ws.on("close", () => {
925
1190
  clients.delete(ws);
926
- console.log(pc2.dim(` Client disconnected (${clients.size} remaining)`));
1191
+ console.log(pc3.dim(` Client disconnected (${clients.size} remaining)`));
927
1192
  });
928
1193
  ws.on("message", (data) => {
929
1194
  try {
@@ -934,34 +1199,43 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
934
1199
  });
935
1200
  });
936
1201
  wss.on("error", (err) => {
937
- console.error(pc2.red(`WebSocket server error: ${err.message}`));
1202
+ console.error(pc3.red(`WebSocket server error: ${err.message}`));
938
1203
  });
939
- console.log(pc2.white(` Hot reload server: ${pc2.bold(`ws://localhost:${port}`)}`));
940
- const iosDir = join2(cwd, "ios");
941
- const androidDir = join2(cwd, "android");
942
- if (existsSync2(iosDir)) {
943
- console.log(pc2.dim(` iOS app should connect to ws://localhost:${port}`));
1204
+ console.log(pc3.white(` Hot reload server: ${pc3.bold(`ws://localhost:${port}`)}`));
1205
+ if (lanIP) {
1206
+ console.log(pc3.white(` LAN address: ${pc3.bold(`ws://${lanIP}:${port}`)}`));
1207
+ }
1208
+ const iosDir = join3(cwd, "ios");
1209
+ const androidDir = join3(cwd, "android");
1210
+ if (existsSync3(iosDir)) {
1211
+ console.log(pc3.dim(` iOS Simulator: ws://localhost:${port}`));
1212
+ if (lanIP) {
1213
+ console.log(pc3.dim(` iOS Device (WiFi): ws://${lanIP}:${port}`));
1214
+ }
944
1215
  }
945
- if (existsSync2(androidDir)) {
946
- console.log(pc2.dim(` Android emulator should connect to ws://10.0.2.2:${port}`));
1216
+ if (existsSync3(androidDir)) {
1217
+ console.log(pc3.dim(` Android emulator: ws://10.0.2.2:${port}`));
1218
+ if (lanIP) {
1219
+ console.log(pc3.dim(` Android Device: ws://${lanIP}:${port}`));
1220
+ }
947
1221
  }
948
- console.log(pc2.dim(" Waiting for app to connect...\n"));
949
- console.log(pc2.white(" Starting Vite build watcher...\n"));
950
- const vite = spawn(
1222
+ console.log(pc3.dim(" Waiting for app to connect...\n"));
1223
+ console.log(pc3.white(" Starting Vite build watcher...\n"));
1224
+ const vite = spawn2(
951
1225
  "bun",
952
1226
  ["run", "vite", "build", "--watch", "--mode", "development"],
953
1227
  { cwd, stdio: "pipe" }
954
1228
  );
955
1229
  vite.stdout?.on("data", (data) => {
956
1230
  const text = data.toString().trim();
957
- if (text) console.log(pc2.dim(` [vite] ${text}`));
1231
+ if (text) console.log(pc3.dim(` [vite] ${text}`));
958
1232
  });
959
1233
  vite.stderr?.on("data", (data) => {
960
1234
  const text = data.toString().trim();
961
- if (text) console.log(pc2.yellow(` [vite] ${text}`));
1235
+ if (text) console.log(pc3.yellow(` [vite] ${text}`));
962
1236
  });
963
1237
  vite.on("error", (err) => {
964
- console.error(pc2.red(`Vite error: ${err.message}`));
1238
+ console.error(pc3.red(`Vite error: ${err.message}`));
965
1239
  });
966
1240
  const watcher = watch(bundlePath, {
967
1241
  persistent: true,
@@ -981,7 +1255,7 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
981
1255
  sent++;
982
1256
  }
983
1257
  }
984
- console.log(pc2.green(` Bundle updated (${Math.round(bundle.length / 1024)}KB) -> sent to ${sent} client(s)`));
1258
+ console.log(pc3.green(` Bundle updated (${Math.round(bundle.length / 1024)}KB) -> sent to ${sent} client(s)`));
985
1259
  } catch {
986
1260
  }
987
1261
  }
@@ -993,7 +1267,7 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
993
1267
  }
994
1268
  }, 3e4);
995
1269
  process.on("SIGINT", () => {
996
- console.log(pc2.yellow("\n Shutting down dev server..."));
1270
+ console.log(pc3.yellow("\n Shutting down dev server..."));
997
1271
  vite.kill();
998
1272
  wss.close();
999
1273
  process.exit(0);
@@ -1001,30 +1275,30 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
1001
1275
  });
1002
1276
 
1003
1277
  // src/commands/run.ts
1004
- import { Command as Command3 } from "commander";
1005
- import { spawn as spawn2, execSync as execSync2 } from "child_process";
1006
- import { existsSync as existsSync3, readdirSync, readFileSync } from "fs";
1007
- import { join as join3 } from "path";
1008
- import pc3 from "picocolors";
1278
+ import { Command as Command4 } from "commander";
1279
+ import { spawn as spawn3, execSync as execSync3 } from "child_process";
1280
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync } from "fs";
1281
+ import { join as join4 } from "path";
1282
+ import pc4 from "picocolors";
1009
1283
  function findAppPath(_buildDir) {
1010
- const derivedDataBase = join3(
1284
+ const derivedDataBase = join4(
1011
1285
  process.env.HOME || "~",
1012
1286
  "Library/Developer/Xcode/DerivedData"
1013
1287
  );
1014
- if (existsSync3(derivedDataBase)) {
1288
+ if (existsSync4(derivedDataBase)) {
1015
1289
  try {
1016
- const projects = readdirSync(derivedDataBase);
1290
+ const projects = readdirSync2(derivedDataBase);
1017
1291
  for (const project of projects.reverse()) {
1018
- const productsDir = join3(
1292
+ const productsDir = join4(
1019
1293
  derivedDataBase,
1020
1294
  project,
1021
1295
  "Build/Products/Debug-iphonesimulator"
1022
1296
  );
1023
- if (existsSync3(productsDir)) {
1024
- const entries = readdirSync(productsDir);
1297
+ if (existsSync4(productsDir)) {
1298
+ const entries = readdirSync2(productsDir);
1025
1299
  const app = entries.find((e) => e.endsWith(".app"));
1026
1300
  if (app) {
1027
- return join3(productsDir, app);
1301
+ return join4(productsDir, app);
1028
1302
  }
1029
1303
  }
1030
1304
  }
@@ -1034,8 +1308,8 @@ function findAppPath(_buildDir) {
1034
1308
  return null;
1035
1309
  }
1036
1310
  function readBundleId(iosDir) {
1037
- const plistPath = join3(iosDir, "Sources", "Info.plist");
1038
- if (existsSync3(plistPath)) {
1311
+ const plistPath = join4(iosDir, "Sources", "Info.plist");
1312
+ if (existsSync4(plistPath)) {
1039
1313
  try {
1040
1314
  const content = readFileSync(plistPath, "utf8");
1041
1315
  const match = content.match(
@@ -1050,34 +1324,34 @@ function readBundleId(iosDir) {
1050
1324
  return "com.vuenative.app";
1051
1325
  }
1052
1326
  function findApkPath(androidDir) {
1053
- const apkDir = join3(androidDir, "app", "build", "outputs", "apk", "debug");
1054
- if (existsSync3(apkDir)) {
1327
+ const apkDir = join4(androidDir, "app", "build", "outputs", "apk", "debug");
1328
+ if (existsSync4(apkDir)) {
1055
1329
  try {
1056
- const entries = readdirSync(apkDir);
1330
+ const entries = readdirSync2(apkDir);
1057
1331
  const apk = entries.find((e) => e.endsWith(".apk") && !e.includes("androidTest"));
1058
1332
  if (apk) {
1059
- return join3(apkDir, apk);
1333
+ return join4(apkDir, apk);
1060
1334
  }
1061
1335
  } catch {
1062
1336
  }
1063
1337
  }
1064
1338
  return null;
1065
1339
  }
1066
- var runCommand = new Command3("run").description("Build and run the app").argument("<platform>", "platform to run on (ios, android)").option("--device", "run on physical device instead of simulator").option("--scheme <scheme>", "Xcode scheme to build").option("--simulator <name>", "simulator name", "iPhone 16").option("--bundle-id <id>", "app bundle identifier").option("--package <name>", "Android package name", "com.vuenative.app").option("--activity <name>", "Android activity name", ".MainActivity").action(async (platform, options) => {
1340
+ var runCommand = new Command4("run").description("Build and run the app").argument("<platform>", "platform to run on (ios, android)").option("--device", "run on physical device instead of simulator").option("--scheme <scheme>", "Xcode scheme to build").option("--simulator <name>", "simulator name", "iPhone 16").option("--bundle-id <id>", "app bundle identifier").option("--package <name>", "Android package name", "com.vuenative.app").option("--activity <name>", "Android activity name", ".MainActivity").action(async (platform, options) => {
1067
1341
  if (platform !== "ios" && platform !== "android") {
1068
- console.error(pc3.red('Platform must be "ios" or "android"'));
1342
+ console.error(pc4.red('Platform must be "ios" or "android"'));
1069
1343
  process.exit(1);
1070
1344
  }
1071
1345
  const cwd = process.cwd();
1072
- console.log(pc3.cyan(`
1346
+ console.log(pc4.cyan(`
1073
1347
  \u{1F4F1} Vue Native \u2014 Run ${platform === "ios" ? "iOS" : "Android"}
1074
1348
  `));
1075
- console.log(pc3.white(" Building JS bundle..."));
1349
+ console.log(pc4.white(" Building JS bundle..."));
1076
1350
  try {
1077
- execSync2("bun run vite build", { cwd, stdio: "inherit" });
1078
- console.log(pc3.green(" \u2713 Bundle built\n"));
1351
+ execSync3("bun run vite build", { cwd, stdio: "inherit" });
1352
+ console.log(pc4.green(" \u2713 Bundle built\n"));
1079
1353
  } catch {
1080
- console.error(pc3.red(" \u2717 Bundle build failed"));
1354
+ console.error(pc4.red(" \u2717 Bundle build failed"));
1081
1355
  process.exit(1);
1082
1356
  }
1083
1357
  if (platform === "ios") {
@@ -1088,14 +1362,14 @@ var runCommand = new Command3("run").description("Build and run the app").argume
1088
1362
  });
1089
1363
  function runIOS(cwd, options) {
1090
1364
  let xcodeProject = null;
1091
- const iosDir = join3(cwd, "ios");
1092
- if (existsSync3(iosDir)) {
1365
+ const iosDir = join4(cwd, "ios");
1366
+ if (existsSync4(iosDir)) {
1093
1367
  for (const ext of [".xcworkspace", ".xcodeproj"]) {
1094
1368
  try {
1095
- const entries = readdirSync(iosDir);
1369
+ const entries = readdirSync2(iosDir);
1096
1370
  const match = entries.find((e) => e.endsWith(ext));
1097
1371
  if (match) {
1098
- xcodeProject = join3(iosDir, match);
1372
+ xcodeProject = join4(iosDir, match);
1099
1373
  break;
1100
1374
  }
1101
1375
  } catch {
@@ -1103,17 +1377,17 @@ function runIOS(cwd, options) {
1103
1377
  }
1104
1378
  }
1105
1379
  if (!xcodeProject) {
1106
- console.log(pc3.yellow(" No Xcode project found in ./ios/"));
1107
- console.log(pc3.dim(" To add iOS support, create an Xcode project in the ios/ directory."));
1108
- console.log(pc3.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1380
+ console.log(pc4.yellow(" No Xcode project found in ./ios/"));
1381
+ console.log(pc4.dim(" To add iOS support, create an Xcode project in the ios/ directory."));
1382
+ console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1109
1383
  return;
1110
1384
  }
1111
1385
  const isWorkspace = xcodeProject.endsWith(".xcworkspace");
1112
1386
  const scheme = options.scheme || xcodeProject.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
1113
1387
  const destination = options.device ? "generic/platform=iOS" : `platform=iOS Simulator,name=${options.simulator}`;
1114
1388
  const projectFlag = isWorkspace ? "-workspace" : "-project";
1115
- console.log(pc3.white(` Building ${scheme} for ${options.device ? "device" : options.simulator}...`));
1116
- const xcodebuild = spawn2(
1389
+ console.log(pc4.white(` Building ${scheme} for ${options.device ? "device" : options.simulator}...`));
1390
+ const xcodebuild = spawn3(
1117
1391
  "xcodebuild",
1118
1392
  [projectFlag, xcodeProject, "-scheme", scheme, "-destination", destination, "build"],
1119
1393
  {
@@ -1125,71 +1399,71 @@ function runIOS(cwd, options) {
1125
1399
  xcodebuild.stderr?.on("data", (data) => {
1126
1400
  const text = data.toString().trim();
1127
1401
  if (text.includes("error:") || text.includes("warning:")) {
1128
- console.log(pc3.dim(` ${text}`));
1402
+ console.log(pc4.dim(` ${text}`));
1129
1403
  }
1130
1404
  });
1131
1405
  xcodebuild.on("close", (code) => {
1132
1406
  if (code !== 0) {
1133
- console.error(pc3.red(` \u2717 Build failed (exit code ${code})`));
1407
+ console.error(pc4.red(` \u2717 Build failed (exit code ${code})`));
1134
1408
  process.exit(1);
1135
1409
  }
1136
- console.log(pc3.green(" \u2713 Build successful\n"));
1410
+ console.log(pc4.green(" \u2713 Build successful\n"));
1137
1411
  if (options.device) {
1138
- console.log(pc3.green(" App built for device. Install via Xcode.\n"));
1412
+ console.log(pc4.green(" App built for device. Install via Xcode.\n"));
1139
1413
  return;
1140
1414
  }
1141
1415
  const simulatorName = options.simulator;
1142
- const bundleId = options.bundleId || readBundleId(join3(cwd, "ios"));
1143
- console.log(pc3.white(` Booting simulator "${simulatorName}"...`));
1416
+ const bundleId = options.bundleId || readBundleId(join4(cwd, "ios"));
1417
+ console.log(pc4.white(` Booting simulator "${simulatorName}"...`));
1144
1418
  try {
1145
- execSync2(`xcrun simctl boot "${simulatorName}"`, { stdio: "pipe" });
1419
+ execSync3(`xcrun simctl boot "${simulatorName}"`, { stdio: "pipe" });
1146
1420
  } catch {
1147
1421
  }
1148
1422
  try {
1149
- execSync2("open -a Simulator", { stdio: "pipe" });
1423
+ execSync3("open -a Simulator", { stdio: "pipe" });
1150
1424
  } catch {
1151
1425
  }
1152
- const appPath = findAppPath(join3(cwd, "ios"));
1426
+ const appPath = findAppPath(join4(cwd, "ios"));
1153
1427
  if (appPath) {
1154
- console.log(pc3.white(` Installing app on simulator...`));
1428
+ console.log(pc4.white(` Installing app on simulator...`));
1155
1429
  try {
1156
- execSync2(`xcrun simctl install booted "${appPath}"`, { stdio: "pipe" });
1157
- console.log(pc3.green(" \u2713 App installed"));
1430
+ execSync3(`xcrun simctl install booted "${appPath}"`, { stdio: "pipe" });
1431
+ console.log(pc4.green(" \u2713 App installed"));
1158
1432
  } catch (err) {
1159
- console.error(pc3.red(` \u2717 Failed to install app: ${err.message}`));
1433
+ console.error(pc4.red(` \u2717 Failed to install app: ${err.message}`));
1160
1434
  process.exit(1);
1161
1435
  }
1162
- console.log(pc3.white(` Launching ${bundleId}...`));
1436
+ console.log(pc4.white(` Launching ${bundleId}...`));
1163
1437
  try {
1164
- execSync2(`xcrun simctl launch booted "${bundleId}"`, { stdio: "pipe" });
1165
- console.log(pc3.green(` \u2713 App launched on ${simulatorName}
1438
+ execSync3(`xcrun simctl launch booted "${bundleId}"`, { stdio: "pipe" });
1439
+ console.log(pc4.green(` \u2713 App launched on ${simulatorName}
1166
1440
  `));
1167
1441
  } catch (err) {
1168
- console.error(pc3.red(` \u2717 Failed to launch app: ${err.message}`));
1442
+ console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
1169
1443
  process.exit(1);
1170
1444
  }
1171
1445
  } else {
1172
- console.log(pc3.yellow(" Could not locate .app bundle in DerivedData."));
1173
- console.log(pc3.dim(" Try running the app from Xcode directly.\n"));
1446
+ console.log(pc4.yellow(" Could not locate .app bundle in DerivedData."));
1447
+ console.log(pc4.dim(" Try running the app from Xcode directly.\n"));
1174
1448
  }
1175
1449
  });
1176
1450
  }
1177
1451
  function runAndroid(cwd, options) {
1178
- const androidDir = join3(cwd, "android");
1179
- if (!existsSync3(androidDir)) {
1180
- console.log(pc3.yellow(" No android/ directory found."));
1181
- console.log(pc3.dim(" To add Android support, create an Android project in the android/ directory."));
1182
- console.log(pc3.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1452
+ const androidDir = join4(cwd, "android");
1453
+ if (!existsSync4(androidDir)) {
1454
+ console.log(pc4.yellow(" No android/ directory found."));
1455
+ console.log(pc4.dim(" To add Android support, create an Android project in the android/ directory."));
1456
+ console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1183
1457
  return;
1184
1458
  }
1185
- const gradlew = join3(androidDir, "gradlew");
1186
- if (!existsSync3(gradlew)) {
1187
- console.error(pc3.red(" \u2717 gradlew not found in android/ directory"));
1188
- console.log(pc3.dim(" Make sure your Android project has the Gradle wrapper.\n"));
1459
+ const gradlew = join4(androidDir, "gradlew");
1460
+ if (!existsSync4(gradlew)) {
1461
+ console.error(pc4.red(" \u2717 gradlew not found in android/ directory"));
1462
+ console.log(pc4.dim(" Make sure your Android project has the Gradle wrapper.\n"));
1189
1463
  process.exit(1);
1190
1464
  }
1191
- console.log(pc3.white(" Building Android app with Gradle..."));
1192
- const gradle = spawn2(
1465
+ console.log(pc4.white(" Building Android app with Gradle..."));
1466
+ const gradle = spawn3(
1193
1467
  "./gradlew",
1194
1468
  ["assembleDebug"],
1195
1469
  {
@@ -1209,48 +1483,48 @@ function runAndroid(cwd, options) {
1209
1483
  gradle.stdout?.on("data", (data) => {
1210
1484
  const text = data.toString().trim();
1211
1485
  if (text) {
1212
- console.log(pc3.dim(` ${text}`));
1486
+ console.log(pc4.dim(` ${text}`));
1213
1487
  }
1214
1488
  });
1215
1489
  gradle.stderr?.on("data", (data) => {
1216
1490
  const text = data.toString().trim();
1217
1491
  if (text.includes("ERROR") || text.includes("FAILURE")) {
1218
- console.log(pc3.red(` ${text}`));
1492
+ console.log(pc4.red(` ${text}`));
1219
1493
  }
1220
1494
  });
1221
1495
  gradle.on("error", (err) => {
1222
- console.error(pc3.red(` Gradle process error: ${err.message}`));
1496
+ console.error(pc4.red(` Gradle process error: ${err.message}`));
1223
1497
  cleanupGradle();
1224
1498
  });
1225
1499
  gradle.on("close", (code) => {
1226
1500
  if (code !== 0) {
1227
- console.error(pc3.red(` \u2717 Gradle build failed (exit code ${code})`));
1501
+ console.error(pc4.red(` \u2717 Gradle build failed (exit code ${code})`));
1228
1502
  process.exit(1);
1229
1503
  }
1230
- console.log(pc3.green(" \u2713 Build successful\n"));
1504
+ console.log(pc4.green(" \u2713 Build successful\n"));
1231
1505
  const apkPath = findApkPath(androidDir);
1232
1506
  if (!apkPath) {
1233
- console.log(pc3.yellow(" Could not locate debug APK."));
1234
- console.log(pc3.dim(" Expected at android/app/build/outputs/apk/debug/\n"));
1507
+ console.log(pc4.yellow(" Could not locate debug APK."));
1508
+ console.log(pc4.dim(" Expected at android/app/build/outputs/apk/debug/\n"));
1235
1509
  return;
1236
1510
  }
1237
- console.log(pc3.white(" Installing APK on device/emulator..."));
1511
+ console.log(pc4.white(" Installing APK on device/emulator..."));
1238
1512
  try {
1239
- execSync2(`adb install -r "${apkPath}"`, { stdio: "pipe" });
1240
- console.log(pc3.green(" \u2713 APK installed"));
1513
+ execSync3(`adb install -r "${apkPath}"`, { stdio: "pipe" });
1514
+ console.log(pc4.green(" \u2713 APK installed"));
1241
1515
  } catch (err) {
1242
- console.error(pc3.red(` \u2717 Failed to install APK: ${err.message}`));
1243
- console.log(pc3.dim(" Make sure an emulator is running or a device is connected (adb devices).\n"));
1516
+ console.error(pc4.red(` \u2717 Failed to install APK: ${err.message}`));
1517
+ console.log(pc4.dim(" Make sure an emulator is running or a device is connected (adb devices).\n"));
1244
1518
  process.exit(1);
1245
1519
  }
1246
1520
  const componentName = `${options.package}/${options.activity}`;
1247
- console.log(pc3.white(` Launching ${componentName}...`));
1521
+ console.log(pc4.white(` Launching ${componentName}...`));
1248
1522
  try {
1249
- execSync2(`adb shell am start -n "${componentName}"`, { stdio: "pipe" });
1250
- console.log(pc3.green(` \u2713 App launched
1523
+ execSync3(`adb shell am start -n "${componentName}"`, { stdio: "pipe" });
1524
+ console.log(pc4.green(` \u2713 App launched
1251
1525
  `));
1252
1526
  } catch (err) {
1253
- console.error(pc3.red(` \u2717 Failed to launch app: ${err.message}`));
1527
+ console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
1254
1528
  process.exit(1);
1255
1529
  }
1256
1530
  });
@@ -1258,6 +1532,7 @@ function runAndroid(cwd, options) {
1258
1532
 
1259
1533
  // src/cli.ts
1260
1534
  program.name("vue-native").description("Vue Native \u2014 build native iOS and Android apps with Vue.js").version("0.1.0");
1535
+ program.addCommand(buildCommand);
1261
1536
  program.addCommand(createCommand);
1262
1537
  program.addCommand(devCommand);
1263
1538
  program.addCommand(runCommand);