@thelacanians/vue-native-cli 0.4.14 → 0.6.0

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.
Files changed (123) hide show
  1. package/dist/cli.js +789 -200
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +156 -5
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +33 -13
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +27 -6
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +9 -2
  6. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  7. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/EventThrottle.kt +57 -0
  8. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  9. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  13. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  14. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  15. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  16. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  17. package/native/android/gradle.properties +1 -0
  18. package/native/android/gradlew +1 -1
  19. package/native/ios/VueNativeCore/Package.swift +1 -1
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +80 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +244 -112
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +19 -2
  23. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +9 -4
  24. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +8 -3
  25. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  26. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  27. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  28. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  29. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  30. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  31. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  32. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  33. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  34. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  35. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  36. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  94. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  95. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  96. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  97. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  98. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  99. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  100. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  101. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  102. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  103. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  104. package/native/shared/VueNativeShared/Package.swift +14 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  115. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  116. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  117. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  118. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  119. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  120. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  121. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  122. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  123. package/package.json +8 -2
package/dist/cli.js CHANGED
@@ -3,29 +3,356 @@
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, macos)").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" && platform !== "macos") {
64
+ console.error(pc.red('Platform must be "ios", "android", or "macos"'));
65
+ process.exit(1);
66
+ }
67
+ const cwd = process.cwd();
68
+ const outputDir = join(cwd, options.output);
69
+ const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
70
+ console.log(pc.cyan(`
71
+ Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${platformLabel})
72
+ `));
73
+ console.log(pc.white(" Building JS bundle for production..."));
74
+ try {
75
+ execSync("bun run vite build --mode production", { cwd, stdio: "inherit" });
76
+ console.log(pc.green(" \u2713 Bundle built\n"));
77
+ } catch {
78
+ console.error(pc.red(" \u2717 Bundle build failed"));
79
+ process.exit(1);
80
+ }
81
+ if (platform === "ios") {
82
+ buildIOS(cwd, outputDir, options);
83
+ } else if (platform === "android") {
84
+ buildAndroid(cwd, outputDir, options);
85
+ } else {
86
+ buildMacOS(cwd, outputDir, options);
87
+ }
88
+ });
89
+ function buildIOS(cwd, outputDir, options) {
90
+ const iosDir = join(cwd, "ios");
91
+ const project = findXcodeProject(iosDir);
92
+ if (!project) {
93
+ console.log(pc.yellow(" No Xcode project found in ./ios/"));
94
+ console.log(pc.dim(" To add iOS support, create an Xcode project in the ios/ directory."));
95
+ console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
96
+ return;
97
+ }
98
+ const projectFlag = project.isWorkspace ? "-workspace" : "-project";
99
+ const scheme = options.scheme || project.path.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
100
+ const configuration = options.mode === "release" ? "Release" : "Debug";
101
+ const archivePath = join(outputDir, `${scheme}.xcarchive`);
102
+ ensureOutputDir(outputDir);
103
+ console.log(pc.white(` Archiving ${scheme} (${configuration})...`));
104
+ console.log(pc.dim(` Archive path: ${archivePath}`));
105
+ const xcodebuild = spawn(
106
+ "xcodebuild",
107
+ [
108
+ projectFlag,
109
+ project.path,
110
+ "-scheme",
111
+ scheme,
112
+ "-configuration",
113
+ configuration,
114
+ "-destination",
115
+ "generic/platform=iOS",
116
+ "-archivePath",
117
+ archivePath,
118
+ "archive"
119
+ ],
120
+ {
121
+ cwd,
122
+ stdio: "pipe",
123
+ env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
124
+ }
125
+ );
126
+ const cleanup = () => {
127
+ if (xcodebuild && !xcodebuild.killed) {
128
+ xcodebuild.kill();
129
+ }
130
+ };
131
+ process.on("exit", cleanup);
132
+ process.on("SIGINT", cleanup);
133
+ process.on("SIGTERM", cleanup);
134
+ xcodebuild.stdout?.on("data", (data) => {
135
+ const text = data.toString().trim();
136
+ if (text.includes("Compiling") || text.includes("Linking") || text.includes("Signing")) {
137
+ console.log(pc.dim(` ${text.split("\n").pop()}`));
138
+ }
139
+ });
140
+ xcodebuild.stderr?.on("data", (data) => {
141
+ const text = data.toString().trim();
142
+ if (text.includes("error:")) {
143
+ console.log(pc.red(` ${text}`));
144
+ } else if (text.includes("warning:")) {
145
+ console.log(pc.yellow(` ${text}`));
146
+ }
147
+ });
148
+ xcodebuild.on("error", (err) => {
149
+ console.error(pc.red(` xcodebuild process error: ${err.message}`));
150
+ cleanup();
151
+ });
152
+ xcodebuild.on("close", (code) => {
153
+ if (code !== 0) {
154
+ console.error(pc.red(` \u2717 Archive failed (exit code ${code})`));
155
+ process.exit(1);
156
+ }
157
+ console.log(pc.green(" \u2713 Archive successful\n"));
158
+ if (existsSync(archivePath)) {
159
+ console.log(pc.green(` Archive: ${archivePath}`));
160
+ console.log(pc.dim(" To export an IPA, open the archive in Xcode Organizer or run:"));
161
+ console.log(pc.dim(` xcodebuild -exportArchive -archivePath "${archivePath}" -exportOptionsPlist ExportOptions.plist -exportPath "${outputDir}"
162
+ `));
163
+ } else {
164
+ console.log(pc.yellow(" Archive path not found. Check Xcode build settings.\n"));
165
+ }
166
+ });
167
+ }
168
+ function buildAndroid(cwd, outputDir, options) {
169
+ const androidDir = join(cwd, "android");
170
+ if (!existsSync(androidDir)) {
171
+ console.log(pc.yellow(" No android/ directory found."));
172
+ console.log(pc.dim(" To add Android support, create an Android project in the android/ directory."));
173
+ console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
174
+ return;
175
+ }
176
+ const gradlew = join(androidDir, "gradlew");
177
+ if (!existsSync(gradlew)) {
178
+ console.error(pc.red(" \u2717 gradlew not found in android/ directory"));
179
+ console.log(pc.dim(" Make sure your Android project has the Gradle wrapper.\n"));
180
+ process.exit(1);
181
+ }
182
+ const gradleTask = options.aab ? "bundleRelease" : "assembleRelease";
183
+ const artifactType = options.aab ? "AAB" : "APK";
184
+ ensureOutputDir(outputDir);
185
+ console.log(pc.white(` Building release ${artifactType} with Gradle...`));
186
+ const gradle = spawn(
187
+ "./gradlew",
188
+ [gradleTask],
189
+ {
190
+ cwd: androidDir,
191
+ stdio: "pipe",
192
+ env: { ...process.env }
193
+ }
194
+ );
195
+ const cleanupGradle = () => {
196
+ if (gradle && !gradle.killed) {
197
+ gradle.kill();
198
+ }
199
+ };
200
+ process.on("exit", cleanupGradle);
201
+ process.on("SIGINT", cleanupGradle);
202
+ process.on("SIGTERM", cleanupGradle);
203
+ gradle.stdout?.on("data", (data) => {
204
+ const text = data.toString().trim();
205
+ if (text) {
206
+ console.log(pc.dim(` ${text}`));
207
+ }
208
+ });
209
+ gradle.stderr?.on("data", (data) => {
210
+ const text = data.toString().trim();
211
+ if (text.includes("ERROR") || text.includes("FAILURE")) {
212
+ console.log(pc.red(` ${text}`));
213
+ }
214
+ });
215
+ gradle.on("error", (err) => {
216
+ console.error(pc.red(` Gradle process error: ${err.message}`));
217
+ cleanupGradle();
218
+ });
219
+ gradle.on("close", (code) => {
220
+ if (code !== 0) {
221
+ console.error(pc.red(` \u2717 Gradle build failed (exit code ${code})`));
222
+ process.exit(1);
223
+ }
224
+ console.log(pc.green(" \u2713 Build successful\n"));
225
+ if (options.aab) {
226
+ const aabPath = findReleaseAab(androidDir);
227
+ if (aabPath) {
228
+ const destPath = join(outputDir, basename(aabPath));
229
+ copyFileSync(aabPath, destPath);
230
+ console.log(pc.green(` AAB copied to: ${destPath}`));
231
+ console.log(pc.dim(" Upload this file to the Google Play Console.\n"));
232
+ } else {
233
+ console.log(pc.yellow(" Could not locate release AAB."));
234
+ console.log(pc.dim(" Expected at android/app/build/outputs/bundle/release/\n"));
235
+ }
236
+ } else {
237
+ const apkPath = findReleaseApk(androidDir);
238
+ if (apkPath) {
239
+ const destPath = join(outputDir, basename(apkPath));
240
+ copyFileSync(apkPath, destPath);
241
+ console.log(pc.green(` APK copied to: ${destPath}`));
242
+ console.log(pc.dim(' Install with: adb install -r "' + destPath + '"\n'));
243
+ } else {
244
+ console.log(pc.yellow(" Could not locate release APK."));
245
+ console.log(pc.dim(" Expected at android/app/build/outputs/apk/release/\n"));
246
+ }
247
+ }
248
+ });
249
+ }
250
+ function buildMacOS(cwd, outputDir, options) {
251
+ const macosDir = join(cwd, "macos");
252
+ const project = findXcodeProject(macosDir);
253
+ if (!project) {
254
+ console.log(pc.yellow(" No Xcode project found in ./macos/"));
255
+ console.log(pc.dim(" To add macOS support, create an Xcode project in the macos/ directory."));
256
+ console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
257
+ return;
258
+ }
259
+ const projectFlag = project.isWorkspace ? "-workspace" : "-project";
260
+ const scheme = options.scheme || project.path.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
261
+ const configuration = options.mode === "release" ? "Release" : "Debug";
262
+ const archivePath = join(outputDir, `${scheme}.xcarchive`);
263
+ ensureOutputDir(outputDir);
264
+ console.log(pc.white(` Archiving ${scheme} (${configuration}) for macOS...`));
265
+ console.log(pc.dim(` Archive path: ${archivePath}`));
266
+ const xcodebuild = spawn(
267
+ "xcodebuild",
268
+ [
269
+ projectFlag,
270
+ project.path,
271
+ "-scheme",
272
+ scheme,
273
+ "-configuration",
274
+ configuration,
275
+ "-destination",
276
+ "generic/platform=macOS",
277
+ "-archivePath",
278
+ archivePath,
279
+ "archive"
280
+ ],
281
+ {
282
+ cwd,
283
+ stdio: "pipe",
284
+ env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
285
+ }
286
+ );
287
+ const cleanup = () => {
288
+ if (xcodebuild && !xcodebuild.killed) {
289
+ xcodebuild.kill();
290
+ }
291
+ };
292
+ process.on("exit", cleanup);
293
+ process.on("SIGINT", cleanup);
294
+ process.on("SIGTERM", cleanup);
295
+ xcodebuild.stdout?.on("data", (data) => {
296
+ const text = data.toString().trim();
297
+ if (text.includes("Compiling") || text.includes("Linking") || text.includes("Signing")) {
298
+ console.log(pc.dim(` ${text.split("\n").pop()}`));
299
+ }
300
+ });
301
+ xcodebuild.stderr?.on("data", (data) => {
302
+ const text = data.toString().trim();
303
+ if (text.includes("error:")) {
304
+ console.log(pc.red(` ${text}`));
305
+ } else if (text.includes("warning:")) {
306
+ console.log(pc.yellow(` ${text}`));
307
+ }
308
+ });
309
+ xcodebuild.on("error", (err) => {
310
+ console.error(pc.red(` xcodebuild process error: ${err.message}`));
311
+ cleanup();
312
+ });
313
+ xcodebuild.on("close", (code) => {
314
+ if (code !== 0) {
315
+ console.error(pc.red(` \u2717 Archive failed (exit code ${code})`));
316
+ process.exit(1);
317
+ }
318
+ console.log(pc.green(" \u2713 Archive successful\n"));
319
+ if (existsSync(archivePath)) {
320
+ console.log(pc.green(` Archive: ${archivePath}`));
321
+ console.log(pc.dim(" To export a .app or .pkg, open the archive in Xcode Organizer or run:"));
322
+ console.log(pc.dim(` xcodebuild -exportArchive -archivePath "${archivePath}" -exportOptionsPlist ExportOptions.plist -exportPath "${outputDir}"
323
+ `));
324
+ } else {
325
+ console.log(pc.yellow(" Archive path not found. Check Xcode build settings.\n"));
326
+ }
327
+ });
328
+ }
329
+
330
+ // src/commands/create.ts
331
+ import { Command as Command2 } from "commander";
8
332
  import { cp, mkdir, writeFile } from "fs/promises";
9
- import { join, dirname } from "path";
333
+ import { join as join2, dirname } from "path";
10
334
  import { fileURLToPath } from "url";
11
- import { existsSync } from "fs";
12
- import pc from "picocolors";
335
+ import { existsSync as existsSync2 } from "fs";
336
+ import pc2 from "picocolors";
13
337
  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) => {
338
+ var JS_PACKAGE_VERSION = "^0.5.0";
339
+ var VITE_PLUGIN_VUE_VERSION = "^6.0.5";
340
+ var VITE_VERSION = "^8.0.0";
341
+ 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
342
  const template = options.template;
16
343
  if (!["blank", "tabs", "drawer"].includes(template)) {
17
- console.error(pc.red(`Invalid template "${template}". Choose: blank, tabs, drawer`));
344
+ console.error(pc2.red(`Invalid template "${template}". Choose: blank, tabs, drawer`));
18
345
  process.exit(1);
19
346
  }
20
- const dir = join(process.cwd(), name);
21
- console.log(pc.cyan(`
22
- Creating Vue Native project: ${pc.bold(name)} (template: ${template})
347
+ const dir = join2(process.cwd(), name);
348
+ console.log(pc2.cyan(`
349
+ Creating Vue Native project: ${pc2.bold(name)} (template: ${template})
23
350
  `));
24
351
  try {
25
352
  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({
353
+ await mkdir(join2(dir, "app"), { recursive: true });
354
+ await mkdir(join2(dir, "app", "pages"), { recursive: true });
355
+ await writeFile(join2(dir, "package.json"), JSON.stringify({
29
356
  name,
30
357
  version: "0.0.1",
31
358
  private: true,
@@ -36,18 +363,18 @@ Creating Vue Native project: ${pc.bold(name)} (template: ${template})
36
363
  typecheck: "tsc --noEmit"
37
364
  },
38
365
  dependencies: {
39
- "@thelacanians/vue-native-runtime": "^0.4.0",
40
- "@thelacanians/vue-native-navigation": "^0.4.0",
366
+ "@thelacanians/vue-native-runtime": JS_PACKAGE_VERSION,
367
+ "@thelacanians/vue-native-navigation": JS_PACKAGE_VERSION,
41
368
  "vue": "^3.5.0"
42
369
  },
43
370
  devDependencies: {
44
- "@thelacanians/vue-native-vite-plugin": "^0.4.0",
45
- "@vitejs/plugin-vue": "^5.0.0",
46
- "vite": "^6.1.0",
371
+ "@thelacanians/vue-native-vite-plugin": JS_PACKAGE_VERSION,
372
+ "@vitejs/plugin-vue": VITE_PLUGIN_VUE_VERSION,
373
+ "vite": VITE_VERSION,
47
374
  "typescript": "^5.7.0"
48
375
  }
49
376
  }, null, 2));
50
- await writeFile(join(dir, "vite.config.ts"), `import { defineConfig } from 'vite'
377
+ await writeFile(join2(dir, "vite.config.ts"), `import { defineConfig } from 'vite'
51
378
  import vue from '@vitejs/plugin-vue'
52
379
  import vueNative from '@thelacanians/vue-native-vite-plugin'
53
380
 
@@ -55,7 +382,7 @@ export default defineConfig({
55
382
  plugins: [vue(), vueNative()],
56
383
  })
57
384
  `);
58
- await writeFile(join(dir, "tsconfig.json"), JSON.stringify({
385
+ await writeFile(join2(dir, "tsconfig.json"), JSON.stringify({
59
386
  compilerOptions: {
60
387
  target: "ES2020",
61
388
  module: "ESNext",
@@ -71,12 +398,12 @@ export default defineConfig({
71
398
  include: ["app/**/*", "env.d.ts"]
72
399
  }, null, 2));
73
400
  await generateTemplateFiles(dir, name, template);
74
- const iosDir = join(dir, "ios");
75
- const iosSrcDir = join(iosDir, "Sources");
401
+ const iosDir = join2(dir, "ios");
402
+ const iosSrcDir = join2(iosDir, "Sources");
76
403
  await mkdir(iosSrcDir, { recursive: true });
77
404
  const xcodeProjectName = name.replace(/[^a-zA-Z0-9]/g, "");
78
405
  const bundleId = `com.vuenative.${name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()}`;
79
- await writeFile(join(iosDir, "project.yml"), `name: ${xcodeProjectName}
406
+ await writeFile(join2(iosDir, "project.yml"), `name: ${xcodeProjectName}
80
407
  options:
81
408
  bundleIdPrefix: com.vuenative
82
409
  deploymentTarget:
@@ -107,7 +434,7 @@ targets:
107
434
  - path: ../dist/vue-native-bundle.js
108
435
  optional: true
109
436
  `);
110
- await writeFile(join(iosSrcDir, "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
437
+ await writeFile(join2(iosSrcDir, "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
111
438
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
112
439
  <plist version="1.0">
113
440
  <dict>
@@ -173,7 +500,7 @@ targets:
173
500
  </dict>
174
501
  </plist>
175
502
  `);
176
- await writeFile(join(iosSrcDir, "AppDelegate.swift"), `import UIKit
503
+ await writeFile(join2(iosSrcDir, "AppDelegate.swift"), `import UIKit
177
504
 
178
505
  @main
179
506
  class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -197,7 +524,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
197
524
  }
198
525
  }
199
526
  `);
200
- await writeFile(join(iosSrcDir, "SceneDelegate.swift"), `import UIKit
527
+ await writeFile(join2(iosSrcDir, "SceneDelegate.swift"), `import UIKit
201
528
  import VueNativeCore
202
529
 
203
530
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -225,29 +552,29 @@ class AppViewController: VueNativeViewController {
225
552
  #endif
226
553
  }
227
554
  `);
228
- const androidDir = join(dir, "android");
229
- const androidAppDir = join(androidDir, "app");
555
+ const androidDir = join2(dir, "android");
556
+ const androidAppDir = join2(androidDir, "app");
230
557
  const androidPkg = `com.vuenative.${name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()}`;
231
558
  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");
559
+ const androidSrcDir = join2(androidAppDir, "src", "main");
560
+ const androidKotlinDir = join2(androidSrcDir, "kotlin", androidPkgPath);
561
+ const androidResValuesDir = join2(androidSrcDir, "res", "values");
562
+ const androidResXmlDir = join2(androidSrcDir, "res", "xml");
563
+ const androidDebugResXmlDir = join2(androidAppDir, "src", "debug", "res", "xml");
564
+ const androidGradleWrapperDir = join2(androidDir, "gradle", "wrapper");
238
565
  await mkdir(androidKotlinDir, { recursive: true });
239
566
  await mkdir(androidResValuesDir, { recursive: true });
240
567
  await mkdir(androidResXmlDir, { recursive: true });
241
568
  await mkdir(androidDebugResXmlDir, { recursive: true });
242
569
  await mkdir(androidGradleWrapperDir, { recursive: true });
243
- await writeFile(join(androidDir, "build.gradle.kts"), `// Top-level build file
570
+ await writeFile(join2(androidDir, "build.gradle.kts"), `// Top-level build file
244
571
  plugins {
245
572
  id("com.android.application") version "8.7.3" apply false
246
573
  id("com.android.library") version "8.7.3" apply false
247
574
  id("org.jetbrains.kotlin.android") version "2.0.21" apply false
248
575
  }
249
576
  `);
250
- await writeFile(join(androidDir, "settings.gradle.kts"), `pluginManagement {
577
+ await writeFile(join2(androidDir, "settings.gradle.kts"), `pluginManagement {
251
578
  repositories {
252
579
  google()
253
580
  mavenCentral()
@@ -273,7 +600,7 @@ dependencyResolutionManagement {
273
600
  rootProject.name = "${name}"
274
601
  include(":app")
275
602
  `);
276
- await writeFile(join(androidAppDir, "build.gradle.kts"), `plugins {
603
+ await writeFile(join2(androidAppDir, "build.gradle.kts"), `plugins {
277
604
  id("com.android.application")
278
605
  id("org.jetbrains.kotlin.android")
279
606
  }
@@ -321,13 +648,13 @@ dependencies {
321
648
  implementation("androidx.core:core-ktx:1.15.0")
322
649
  }
323
650
  `);
324
- await writeFile(join(androidAppDir, "proguard-rules.pro"), `# Vue Native
651
+ await writeFile(join2(androidAppDir, "proguard-rules.pro"), `# Vue Native
325
652
  -keep class com.vuenative.** { *; }
326
653
 
327
654
  # J2V8
328
655
  -keep class com.eclipsesource.v8.** { *; }
329
656
  `);
330
- await writeFile(join(androidSrcDir, "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
657
+ await writeFile(join2(androidSrcDir, "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
331
658
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
332
659
  <uses-permission android:name="android.permission.INTERNET" />
333
660
 
@@ -350,12 +677,12 @@ dependencies {
350
677
  </application>
351
678
  </manifest>
352
679
  `);
353
- await writeFile(join(androidResValuesDir, "strings.xml"), `<?xml version="1.0" encoding="utf-8"?>
680
+ await writeFile(join2(androidResValuesDir, "strings.xml"), `<?xml version="1.0" encoding="utf-8"?>
354
681
  <resources>
355
682
  <string name="app_name">${name}</string>
356
683
  </resources>
357
684
  `);
358
- await writeFile(join(androidResValuesDir, "themes.xml"), `<?xml version="1.0" encoding="utf-8"?>
685
+ await writeFile(join2(androidResValuesDir, "themes.xml"), `<?xml version="1.0" encoding="utf-8"?>
359
686
  <resources>
360
687
  <style name="Theme.VueNative" parent="Theme.MaterialComponents.Light.NoActionBar">
361
688
  <item name="colorPrimary">#4F46E5</item>
@@ -368,12 +695,12 @@ dependencies {
368
695
  </style>
369
696
  </resources>
370
697
  `);
371
- await writeFile(join(androidResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
698
+ await writeFile(join2(androidResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
372
699
  <network-security-config>
373
700
  <base-config cleartextTrafficPermitted="false" />
374
701
  </network-security-config>
375
702
  `);
376
- await writeFile(join(androidDebugResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
703
+ await writeFile(join2(androidDebugResXmlDir, "network_security_config.xml"), `<?xml version="1.0" encoding="utf-8"?>
377
704
  <network-security-config>
378
705
  <domain-config cleartextTrafficPermitted="true">
379
706
  <domain includeSubdomains="true">localhost</domain>
@@ -382,7 +709,7 @@ dependencies {
382
709
  </domain-config>
383
710
  </network-security-config>
384
711
  `);
385
- await writeFile(join(androidKotlinDir, "MainActivity.kt"), `package ${androidPkg}
712
+ await writeFile(join2(androidKotlinDir, "MainActivity.kt"), `package ${androidPkg}
386
713
 
387
714
  import com.vuenative.core.VueNativeActivity
388
715
 
@@ -396,20 +723,20 @@ class MainActivity : VueNativeActivity() {
396
723
  }
397
724
  }
398
725
  `);
399
- await writeFile(join(androidDir, "gradle.properties"), `# Project-wide Gradle settings
726
+ await writeFile(join2(androidDir, "gradle.properties"), `# Project-wide Gradle settings
400
727
  org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
401
728
  android.useAndroidX=true
402
729
  kotlin.code.style=official
403
730
  android.nonTransitiveRClass=true
404
731
  `);
405
- await writeFile(join(androidGradleWrapperDir, "gradle-wrapper.properties"), `distributionBase=GRADLE_USER_HOME
732
+ await writeFile(join2(androidGradleWrapperDir, "gradle-wrapper.properties"), `distributionBase=GRADLE_USER_HOME
406
733
  distributionPath=wrapper/dists
407
734
  distributionUrl=https\\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
408
735
  networkTimeout=10000
409
736
  zipStoreBase=GRADLE_USER_HOME
410
737
  zipStorePath=wrapper/dists
411
738
  `);
412
- await writeFile(join(dir, "vue-native.config.ts"), `import { defineConfig } from '@thelacanians/vue-native-cli'
739
+ await writeFile(join2(dir, "vue-native.config.ts"), `import { defineConfig } from '@thelacanians/vue-native-cli'
413
740
 
414
741
  export default defineConfig({
415
742
  name: '${name}',
@@ -424,7 +751,7 @@ export default defineConfig({
424
751
  },
425
752
  })
426
753
  `);
427
- await writeFile(join(dir, "env.d.ts"), `/// <reference types="vite/client" />
754
+ await writeFile(join2(dir, "env.d.ts"), `/// <reference types="vite/client" />
428
755
  declare module '*.vue' {
429
756
  import type { DefineComponent } from '@thelacanians/vue-native-runtime'
430
757
  const component: DefineComponent<{}, {}, any>
@@ -432,7 +759,7 @@ declare module '*.vue' {
432
759
  }
433
760
  declare const __DEV__: boolean
434
761
  `);
435
- await writeFile(join(dir, ".gitignore"), `node_modules/
762
+ await writeFile(join2(dir, ".gitignore"), `node_modules/
436
763
  dist/
437
764
  *.xcuserstate
438
765
  *.xcuserdatad/
@@ -455,30 +782,30 @@ local.properties
455
782
  *.jks
456
783
  `);
457
784
  const cliDir = dirname(dirname(fileURLToPath(import.meta.url)));
458
- const bundledNative = join(cliDir, "native");
459
- if (existsSync(bundledNative)) {
460
- const nativeDir = join(dir, "native");
785
+ const bundledNative = join2(cliDir, "native");
786
+ if (existsSync2(bundledNative)) {
787
+ const nativeDir = join2(dir, "native");
461
788
  await cp(bundledNative, nativeDir, { recursive: true });
462
- console.log(pc.dim(" Bundled native/ copied as fallback.\n"));
463
- }
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"));
789
+ console.log(pc2.dim(" Bundled native/ copied as fallback.\n"));
790
+ }
791
+ console.log(pc2.green(" Project created successfully!\n"));
792
+ console.log(pc2.white(" Next steps:\n"));
793
+ console.log(pc2.white(` cd ${name}`));
794
+ console.log(pc2.white(" bun install"));
795
+ console.log(pc2.white(" vue-native dev\n"));
796
+ console.log(pc2.white(" To run on iOS:"));
797
+ console.log(pc2.white(" vue-native run ios\n"));
798
+ console.log(pc2.white(" To run on Android:"));
799
+ console.log(pc2.dim(" Open android/ in Android Studio, or run:"));
800
+ console.log(pc2.dim(" cd android && gradle wrapper && cd .."));
801
+ console.log(pc2.white(" vue-native run android\n"));
475
802
  } catch (err) {
476
- console.error(pc.red(`Error creating project: ${err.message}`));
803
+ console.error(pc2.red(`Error creating project: ${err.message}`));
477
804
  process.exit(1);
478
805
  }
479
806
  });
480
807
  async function generateTemplateFiles(dir, name, template) {
481
- const pagesDir = join(dir, "app", "pages");
808
+ const pagesDir = join2(dir, "app", "pages");
482
809
  if (template === "blank") {
483
810
  await generateBlankTemplate(dir, pagesDir);
484
811
  } else if (template === "tabs") {
@@ -488,7 +815,7 @@ async function generateTemplateFiles(dir, name, template) {
488
815
  }
489
816
  }
490
817
  async function generateBlankTemplate(dir, pagesDir) {
491
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
818
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
492
819
  import { createRouter } from '@thelacanians/vue-native-navigation'
493
820
  import App from './App.vue'
494
821
  import Home from './pages/Home.vue'
@@ -501,7 +828,7 @@ const app = createApp(App)
501
828
  app.use(router)
502
829
  app.start()
503
830
  `);
504
- await writeFile(join(dir, "app", "App.vue"), `<template>
831
+ await writeFile(join2(dir, "app", "App.vue"), `<template>
505
832
  <VSafeArea :style="{ flex: 1, backgroundColor: '#ffffff' }">
506
833
  <RouterView />
507
834
  </VSafeArea>
@@ -511,7 +838,7 @@ app.start()
511
838
  import { RouterView } from '@thelacanians/vue-native-navigation'
512
839
  </script>
513
840
  `);
514
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
841
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
515
842
  import { ref } from 'vue'
516
843
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
517
844
 
@@ -563,13 +890,13 @@ const styles = createStyleSheet({
563
890
  `);
564
891
  }
565
892
  async function generateTabsTemplate(dir, pagesDir) {
566
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
893
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
567
894
  import App from './App.vue'
568
895
 
569
896
  const app = createApp(App)
570
897
  app.start()
571
898
  `);
572
- await writeFile(join(dir, "app", "App.vue"), `<script setup lang="ts">
899
+ await writeFile(join2(dir, "app", "App.vue"), `<script setup lang="ts">
573
900
  import { createTabNavigator } from '@thelacanians/vue-native-navigation'
574
901
  import Home from './pages/Home.vue'
575
902
  import Settings from './pages/Settings.vue'
@@ -588,7 +915,7 @@ const { TabNavigator } = createTabNavigator()
588
915
  </VSafeArea>
589
916
  </template>
590
917
  `);
591
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
918
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
592
919
  import { ref } from 'vue'
593
920
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
594
921
 
@@ -632,7 +959,7 @@ const styles = createStyleSheet({
632
959
  </VView>
633
960
  </template>
634
961
  `);
635
- await writeFile(join(pagesDir, "Settings.vue"), `<script setup lang="ts">
962
+ await writeFile(join2(pagesDir, "Settings.vue"), `<script setup lang="ts">
636
963
  import { ref } from 'vue'
637
964
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
638
965
 
@@ -676,13 +1003,13 @@ const styles = createStyleSheet({
676
1003
  `);
677
1004
  }
678
1005
  async function generateDrawerTemplate(dir, pagesDir) {
679
- await writeFile(join(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
1006
+ await writeFile(join2(dir, "app", "main.ts"), `import { createApp } from '@thelacanians/vue-native-runtime'
680
1007
  import App from './App.vue'
681
1008
 
682
1009
  const app = createApp(App)
683
1010
  app.start()
684
1011
  `);
685
- await writeFile(join(dir, "app", "App.vue"), `<script setup lang="ts">
1012
+ await writeFile(join2(dir, "app", "App.vue"), `<script setup lang="ts">
686
1013
  import { createDrawerNavigator } from '@thelacanians/vue-native-navigation'
687
1014
  import Home from './pages/Home.vue'
688
1015
  import About from './pages/About.vue'
@@ -701,7 +1028,7 @@ const { DrawerNavigator } = createDrawerNavigator()
701
1028
  </VSafeArea>
702
1029
  </template>
703
1030
  `);
704
- await writeFile(join(pagesDir, "Home.vue"), `<script setup lang="ts">
1031
+ await writeFile(join2(pagesDir, "Home.vue"), `<script setup lang="ts">
705
1032
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
706
1033
  import { useDrawer } from '@thelacanians/vue-native-navigation'
707
1034
 
@@ -754,7 +1081,7 @@ const styles = createStyleSheet({
754
1081
  </VView>
755
1082
  </template>
756
1083
  `);
757
- await writeFile(join(pagesDir, "About.vue"), `<script setup lang="ts">
1084
+ await writeFile(join2(pagesDir, "About.vue"), `<script setup lang="ts">
758
1085
  import { createStyleSheet } from '@thelacanians/vue-native-runtime'
759
1086
  import { useDrawer } from '@thelacanians/vue-native-navigation'
760
1087
 
@@ -810,19 +1137,19 @@ const styles = createStyleSheet({
810
1137
  }
811
1138
 
812
1139
  // src/commands/dev.ts
813
- import { Command as Command2 } from "commander";
814
- import { spawn, execSync } from "child_process";
1140
+ import { Command as Command3 } from "commander";
1141
+ import { spawn as spawn2, execSync as execSync2 } from "child_process";
815
1142
  import { readFile } from "fs/promises";
816
- import { existsSync as existsSync2 } from "fs";
817
- import { join as join2 } from "path";
1143
+ import { existsSync as existsSync3 } from "fs";
1144
+ import { join as join3 } from "path";
818
1145
  import { watch } from "chokidar";
819
1146
  import { WebSocketServer, WebSocket } from "ws";
820
- import pc2 from "picocolors";
1147
+ import pc3 from "picocolors";
821
1148
  var DEFAULT_PORT = 8174;
822
1149
  var BUNDLE_FILE = "dist/vue-native-bundle.js";
823
1150
  function detectIOSSimulators() {
824
1151
  try {
825
- const output = execSync("xcrun simctl list devices available -j", {
1152
+ const output = execSync2("xcrun simctl list devices available -j", {
826
1153
  stdio: "pipe",
827
1154
  encoding: "utf8"
828
1155
  });
@@ -847,33 +1174,33 @@ function detectIOSSimulators() {
847
1174
  }
848
1175
  function bootSimulator(udid) {
849
1176
  try {
850
- execSync(`xcrun simctl boot "${udid}"`, { stdio: "pipe" });
1177
+ execSync2(`xcrun simctl boot "${udid}"`, { stdio: "pipe" });
851
1178
  } catch {
852
1179
  }
853
1180
  try {
854
- execSync("open -a Simulator", { stdio: "pipe" });
1181
+ execSync2("open -a Simulator", { stdio: "pipe" });
855
1182
  } catch {
856
1183
  }
857
1184
  }
858
1185
  function detectAndroidEmulators() {
859
1186
  try {
860
- const output = execSync("adb devices", { stdio: "pipe", encoding: "utf8" });
1187
+ const output = execSync2("adb devices", { stdio: "pipe", encoding: "utf8" });
861
1188
  const lines = output.split("\n").filter((l) => l.includes("device") && !l.startsWith("List"));
862
1189
  return lines.map((l) => l.split(" ")[0]).filter(Boolean);
863
1190
  } catch {
864
1191
  return [];
865
1192
  }
866
1193
  }
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) => {
1194
+ 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
1195
  const port = parseInt(options.port, 10);
869
1196
  const cwd = process.cwd();
870
- const bundlePath = join2(cwd, BUNDLE_FILE);
871
- console.log(pc2.cyan("\n Vue Native Dev Server\n"));
1197
+ const bundlePath = join3(cwd, BUNDLE_FILE);
1198
+ console.log(pc3.cyan("\n Vue Native Dev Server\n"));
872
1199
  if (options.ios) {
873
- console.log(pc2.white(" Detecting iOS Simulators..."));
1200
+ console.log(pc3.white(" Detecting iOS Simulators..."));
874
1201
  const simulators = detectIOSSimulators();
875
1202
  if (simulators.length === 0) {
876
- console.log(pc2.yellow(" No iOS Simulators found. Install Xcode and create a simulator."));
1203
+ console.log(pc3.yellow(" No iOS Simulators found. Install Xcode and create a simulator."));
877
1204
  } else {
878
1205
  let target = simulators.find((s) => s.state === "Booted");
879
1206
  if (!target && options.simulator) {
@@ -884,46 +1211,69 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
884
1211
  }
885
1212
  if (target) {
886
1213
  if (target.state !== "Booted") {
887
- console.log(pc2.white(` Booting ${target.name}...`));
1214
+ console.log(pc3.white(` Booting ${target.name}...`));
888
1215
  bootSimulator(target.udid);
889
1216
  }
890
- console.log(pc2.green(` iOS Simulator ready: ${target.name}`));
1217
+ console.log(pc3.green(` iOS Simulator ready: ${target.name}`));
891
1218
  }
892
1219
  }
893
1220
  console.log();
894
1221
  }
895
1222
  if (options.android) {
896
- console.log(pc2.white(" Detecting Android emulators..."));
1223
+ console.log(pc3.white(" Detecting Android emulators..."));
897
1224
  const emulators = detectAndroidEmulators();
898
1225
  if (emulators.length === 0) {
899
- console.log(pc2.yellow(" No Android emulators detected. Start one via Android Studio or `emulator -avd <name>`."));
1226
+ console.log(pc3.yellow(" No Android emulators detected. Start one via Android Studio or `emulator -avd <name>`."));
900
1227
  } else {
901
- console.log(pc2.green(` Android emulator(s) connected: ${emulators.join(", ")}`));
1228
+ console.log(pc3.green(` Android emulator(s) connected: ${emulators.join(", ")}`));
902
1229
  }
903
1230
  console.log();
904
1231
  }
1232
+ let lanIP = "";
1233
+ try {
1234
+ const nets = await import("os").then((os) => os.networkInterfaces());
1235
+ for (const ifaces of Object.values(nets)) {
1236
+ for (const iface of ifaces ?? []) {
1237
+ if (iface.family === "IPv4" && !iface.internal) {
1238
+ lanIP = iface.address;
1239
+ break;
1240
+ }
1241
+ }
1242
+ if (lanIP) break;
1243
+ }
1244
+ } catch {
1245
+ }
1246
+ const isPrivateOrLocal = (addr) => {
1247
+ if (!addr) return false;
1248
+ const ip = addr.replace(/^::ffff:/, "");
1249
+ if (ip === "127.0.0.1" || ip === "::1") return true;
1250
+ if (ip === "10.0.2.2") return true;
1251
+ if (ip.startsWith("10.")) return true;
1252
+ if (ip.startsWith("192.168.")) return true;
1253
+ if (/^172\.(1[6-9]|2\d|3[01])\./.test(ip)) return true;
1254
+ return false;
1255
+ };
905
1256
  const wss = new WebSocketServer({
906
1257
  port,
907
1258
  verifyClient: (info) => {
908
- const addr = info.req.socket.remoteAddress;
909
- return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
1259
+ return isPrivateOrLocal(info.req.socket.remoteAddress);
910
1260
  }
911
1261
  });
912
1262
  const clients = /* @__PURE__ */ new Set();
913
1263
  wss.on("connection", (ws) => {
914
1264
  clients.add(ws);
915
1265
  ws.send(JSON.stringify({ type: "connected" }));
916
- console.log(pc2.green(` Client connected (${clients.size} total)`));
1266
+ console.log(pc3.green(` Client connected (${clients.size} total)`));
917
1267
  readFile(bundlePath, "utf8").then((bundle) => {
918
1268
  if (ws.readyState === WebSocket.OPEN) {
919
1269
  ws.send(JSON.stringify({ type: "bundle", bundle }));
920
- console.log(pc2.dim(` Sent bundle to new client (${Math.round(bundle.length / 1024)}KB)`));
1270
+ console.log(pc3.dim(` Sent bundle to new client (${Math.round(bundle.length / 1024)}KB)`));
921
1271
  }
922
1272
  }).catch(() => {
923
1273
  });
924
1274
  ws.on("close", () => {
925
1275
  clients.delete(ws);
926
- console.log(pc2.dim(` Client disconnected (${clients.size} remaining)`));
1276
+ console.log(pc3.dim(` Client disconnected (${clients.size} remaining)`));
927
1277
  });
928
1278
  ws.on("message", (data) => {
929
1279
  try {
@@ -934,34 +1284,43 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
934
1284
  });
935
1285
  });
936
1286
  wss.on("error", (err) => {
937
- console.error(pc2.red(`WebSocket server error: ${err.message}`));
1287
+ console.error(pc3.red(`WebSocket server error: ${err.message}`));
938
1288
  });
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}`));
1289
+ console.log(pc3.white(` Hot reload server: ${pc3.bold(`ws://localhost:${port}`)}`));
1290
+ if (lanIP) {
1291
+ console.log(pc3.white(` LAN address: ${pc3.bold(`ws://${lanIP}:${port}`)}`));
1292
+ }
1293
+ const iosDir = join3(cwd, "ios");
1294
+ const androidDir = join3(cwd, "android");
1295
+ if (existsSync3(iosDir)) {
1296
+ console.log(pc3.dim(` iOS Simulator: ws://localhost:${port}`));
1297
+ if (lanIP) {
1298
+ console.log(pc3.dim(` iOS Device (WiFi): ws://${lanIP}:${port}`));
1299
+ }
944
1300
  }
945
- if (existsSync2(androidDir)) {
946
- console.log(pc2.dim(` Android emulator should connect to ws://10.0.2.2:${port}`));
1301
+ if (existsSync3(androidDir)) {
1302
+ console.log(pc3.dim(` Android emulator: ws://10.0.2.2:${port}`));
1303
+ if (lanIP) {
1304
+ console.log(pc3.dim(` Android Device: ws://${lanIP}:${port}`));
1305
+ }
947
1306
  }
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(
1307
+ console.log(pc3.dim(" Waiting for app to connect...\n"));
1308
+ console.log(pc3.white(" Starting Vite build watcher...\n"));
1309
+ const vite = spawn2(
951
1310
  "bun",
952
1311
  ["run", "vite", "build", "--watch", "--mode", "development"],
953
1312
  { cwd, stdio: "pipe" }
954
1313
  );
955
1314
  vite.stdout?.on("data", (data) => {
956
1315
  const text = data.toString().trim();
957
- if (text) console.log(pc2.dim(` [vite] ${text}`));
1316
+ if (text) console.log(pc3.dim(` [vite] ${text}`));
958
1317
  });
959
1318
  vite.stderr?.on("data", (data) => {
960
1319
  const text = data.toString().trim();
961
- if (text) console.log(pc2.yellow(` [vite] ${text}`));
1320
+ if (text) console.log(pc3.yellow(` [vite] ${text}`));
962
1321
  });
963
1322
  vite.on("error", (err) => {
964
- console.error(pc2.red(`Vite error: ${err.message}`));
1323
+ console.error(pc3.red(`Vite error: ${err.message}`));
965
1324
  });
966
1325
  const watcher = watch(bundlePath, {
967
1326
  persistent: true,
@@ -981,7 +1340,7 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
981
1340
  sent++;
982
1341
  }
983
1342
  }
984
- console.log(pc2.green(` Bundle updated (${Math.round(bundle.length / 1024)}KB) -> sent to ${sent} client(s)`));
1343
+ console.log(pc3.green(` Bundle updated (${Math.round(bundle.length / 1024)}KB) -> sent to ${sent} client(s)`));
985
1344
  } catch {
986
1345
  }
987
1346
  }
@@ -993,7 +1352,7 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
993
1352
  }
994
1353
  }, 3e4);
995
1354
  process.on("SIGINT", () => {
996
- console.log(pc2.yellow("\n Shutting down dev server..."));
1355
+ console.log(pc3.yellow("\n Shutting down dev server..."));
997
1356
  vite.kill();
998
1357
  wss.close();
999
1358
  process.exit(0);
@@ -1001,30 +1360,30 @@ var devCommand = new Command2("dev").description("Start the Vue Native dev serve
1001
1360
  });
1002
1361
 
1003
1362
  // 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";
1363
+ import { Command as Command4 } from "commander";
1364
+ import { spawn as spawn3, execSync as execSync3 } from "child_process";
1365
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync } from "fs";
1366
+ import { join as join4 } from "path";
1367
+ import pc4 from "picocolors";
1009
1368
  function findAppPath(_buildDir) {
1010
- const derivedDataBase = join3(
1369
+ const derivedDataBase = join4(
1011
1370
  process.env.HOME || "~",
1012
1371
  "Library/Developer/Xcode/DerivedData"
1013
1372
  );
1014
- if (existsSync3(derivedDataBase)) {
1373
+ if (existsSync4(derivedDataBase)) {
1015
1374
  try {
1016
- const projects = readdirSync(derivedDataBase);
1375
+ const projects = readdirSync2(derivedDataBase);
1017
1376
  for (const project of projects.reverse()) {
1018
- const productsDir = join3(
1377
+ const productsDir = join4(
1019
1378
  derivedDataBase,
1020
1379
  project,
1021
1380
  "Build/Products/Debug-iphonesimulator"
1022
1381
  );
1023
- if (existsSync3(productsDir)) {
1024
- const entries = readdirSync(productsDir);
1382
+ if (existsSync4(productsDir)) {
1383
+ const entries = readdirSync2(productsDir);
1025
1384
  const app = entries.find((e) => e.endsWith(".app"));
1026
1385
  if (app) {
1027
- return join3(productsDir, app);
1386
+ return join4(productsDir, app);
1028
1387
  }
1029
1388
  }
1030
1389
  }
@@ -1034,8 +1393,8 @@ function findAppPath(_buildDir) {
1034
1393
  return null;
1035
1394
  }
1036
1395
  function readBundleId(iosDir) {
1037
- const plistPath = join3(iosDir, "Sources", "Info.plist");
1038
- if (existsSync3(plistPath)) {
1396
+ const plistPath = join4(iosDir, "Sources", "Info.plist");
1397
+ if (existsSync4(plistPath)) {
1039
1398
  try {
1040
1399
  const content = readFileSync(plistPath, "utf8");
1041
1400
  const match = content.match(
@@ -1050,52 +1409,55 @@ function readBundleId(iosDir) {
1050
1409
  return "com.vuenative.app";
1051
1410
  }
1052
1411
  function findApkPath(androidDir) {
1053
- const apkDir = join3(androidDir, "app", "build", "outputs", "apk", "debug");
1054
- if (existsSync3(apkDir)) {
1412
+ const apkDir = join4(androidDir, "app", "build", "outputs", "apk", "debug");
1413
+ if (existsSync4(apkDir)) {
1055
1414
  try {
1056
- const entries = readdirSync(apkDir);
1415
+ const entries = readdirSync2(apkDir);
1057
1416
  const apk = entries.find((e) => e.endsWith(".apk") && !e.includes("androidTest"));
1058
1417
  if (apk) {
1059
- return join3(apkDir, apk);
1418
+ return join4(apkDir, apk);
1060
1419
  }
1061
1420
  } catch {
1062
1421
  }
1063
1422
  }
1064
1423
  return null;
1065
1424
  }
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) => {
1067
- if (platform !== "ios" && platform !== "android") {
1068
- console.error(pc3.red('Platform must be "ios" or "android"'));
1425
+ var runCommand = new Command4("run").description("Build and run the app").argument("<platform>", "platform to run on (ios, android, macos)").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) => {
1426
+ if (platform !== "ios" && platform !== "android" && platform !== "macos") {
1427
+ console.error(pc4.red('Platform must be "ios", "android", or "macos"'));
1069
1428
  process.exit(1);
1070
1429
  }
1071
1430
  const cwd = process.cwd();
1072
- console.log(pc3.cyan(`
1073
- \u{1F4F1} Vue Native \u2014 Run ${platform === "ios" ? "iOS" : "Android"}
1431
+ const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
1432
+ console.log(pc4.cyan(`
1433
+ \u{1F4F1} Vue Native \u2014 Run ${platformLabel}
1074
1434
  `));
1075
- console.log(pc3.white(" Building JS bundle..."));
1435
+ console.log(pc4.white(" Building JS bundle..."));
1076
1436
  try {
1077
- execSync2("bun run vite build", { cwd, stdio: "inherit" });
1078
- console.log(pc3.green(" \u2713 Bundle built\n"));
1437
+ execSync3("bun run vite build", { cwd, stdio: "inherit" });
1438
+ console.log(pc4.green(" \u2713 Bundle built\n"));
1079
1439
  } catch {
1080
- console.error(pc3.red(" \u2717 Bundle build failed"));
1440
+ console.error(pc4.red(" \u2717 Bundle build failed"));
1081
1441
  process.exit(1);
1082
1442
  }
1083
1443
  if (platform === "ios") {
1084
1444
  runIOS(cwd, options);
1085
- } else {
1445
+ } else if (platform === "android") {
1086
1446
  runAndroid(cwd, options);
1447
+ } else {
1448
+ runMacOS(cwd, options);
1087
1449
  }
1088
1450
  });
1089
1451
  function runIOS(cwd, options) {
1090
1452
  let xcodeProject = null;
1091
- const iosDir = join3(cwd, "ios");
1092
- if (existsSync3(iosDir)) {
1453
+ const iosDir = join4(cwd, "ios");
1454
+ if (existsSync4(iosDir)) {
1093
1455
  for (const ext of [".xcworkspace", ".xcodeproj"]) {
1094
1456
  try {
1095
- const entries = readdirSync(iosDir);
1457
+ const entries = readdirSync2(iosDir);
1096
1458
  const match = entries.find((e) => e.endsWith(ext));
1097
1459
  if (match) {
1098
- xcodeProject = join3(iosDir, match);
1460
+ xcodeProject = join4(iosDir, match);
1099
1461
  break;
1100
1462
  }
1101
1463
  } catch {
@@ -1103,17 +1465,17 @@ function runIOS(cwd, options) {
1103
1465
  }
1104
1466
  }
1105
1467
  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"));
1468
+ console.log(pc4.yellow(" No Xcode project found in ./ios/"));
1469
+ console.log(pc4.dim(" To add iOS support, create an Xcode project in the ios/ directory."));
1470
+ console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1109
1471
  return;
1110
1472
  }
1111
1473
  const isWorkspace = xcodeProject.endsWith(".xcworkspace");
1112
1474
  const scheme = options.scheme || xcodeProject.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
1113
1475
  const destination = options.device ? "generic/platform=iOS" : `platform=iOS Simulator,name=${options.simulator}`;
1114
1476
  const projectFlag = isWorkspace ? "-workspace" : "-project";
1115
- console.log(pc3.white(` Building ${scheme} for ${options.device ? "device" : options.simulator}...`));
1116
- const xcodebuild = spawn2(
1477
+ console.log(pc4.white(` Building ${scheme} for ${options.device ? "device" : options.simulator}...`));
1478
+ const xcodebuild = spawn3(
1117
1479
  "xcodebuild",
1118
1480
  [projectFlag, xcodeProject, "-scheme", scheme, "-destination", destination, "build"],
1119
1481
  {
@@ -1125,71 +1487,71 @@ function runIOS(cwd, options) {
1125
1487
  xcodebuild.stderr?.on("data", (data) => {
1126
1488
  const text = data.toString().trim();
1127
1489
  if (text.includes("error:") || text.includes("warning:")) {
1128
- console.log(pc3.dim(` ${text}`));
1490
+ console.log(pc4.dim(` ${text}`));
1129
1491
  }
1130
1492
  });
1131
1493
  xcodebuild.on("close", (code) => {
1132
1494
  if (code !== 0) {
1133
- console.error(pc3.red(` \u2717 Build failed (exit code ${code})`));
1495
+ console.error(pc4.red(` \u2717 Build failed (exit code ${code})`));
1134
1496
  process.exit(1);
1135
1497
  }
1136
- console.log(pc3.green(" \u2713 Build successful\n"));
1498
+ console.log(pc4.green(" \u2713 Build successful\n"));
1137
1499
  if (options.device) {
1138
- console.log(pc3.green(" App built for device. Install via Xcode.\n"));
1500
+ console.log(pc4.green(" App built for device. Install via Xcode.\n"));
1139
1501
  return;
1140
1502
  }
1141
1503
  const simulatorName = options.simulator;
1142
- const bundleId = options.bundleId || readBundleId(join3(cwd, "ios"));
1143
- console.log(pc3.white(` Booting simulator "${simulatorName}"...`));
1504
+ const bundleId = options.bundleId || readBundleId(join4(cwd, "ios"));
1505
+ console.log(pc4.white(` Booting simulator "${simulatorName}"...`));
1144
1506
  try {
1145
- execSync2(`xcrun simctl boot "${simulatorName}"`, { stdio: "pipe" });
1507
+ execSync3(`xcrun simctl boot "${simulatorName}"`, { stdio: "pipe" });
1146
1508
  } catch {
1147
1509
  }
1148
1510
  try {
1149
- execSync2("open -a Simulator", { stdio: "pipe" });
1511
+ execSync3("open -a Simulator", { stdio: "pipe" });
1150
1512
  } catch {
1151
1513
  }
1152
- const appPath = findAppPath(join3(cwd, "ios"));
1514
+ const appPath = findAppPath(join4(cwd, "ios"));
1153
1515
  if (appPath) {
1154
- console.log(pc3.white(` Installing app on simulator...`));
1516
+ console.log(pc4.white(` Installing app on simulator...`));
1155
1517
  try {
1156
- execSync2(`xcrun simctl install booted "${appPath}"`, { stdio: "pipe" });
1157
- console.log(pc3.green(" \u2713 App installed"));
1518
+ execSync3(`xcrun simctl install booted "${appPath}"`, { stdio: "pipe" });
1519
+ console.log(pc4.green(" \u2713 App installed"));
1158
1520
  } catch (err) {
1159
- console.error(pc3.red(` \u2717 Failed to install app: ${err.message}`));
1521
+ console.error(pc4.red(` \u2717 Failed to install app: ${err.message}`));
1160
1522
  process.exit(1);
1161
1523
  }
1162
- console.log(pc3.white(` Launching ${bundleId}...`));
1524
+ console.log(pc4.white(` Launching ${bundleId}...`));
1163
1525
  try {
1164
- execSync2(`xcrun simctl launch booted "${bundleId}"`, { stdio: "pipe" });
1165
- console.log(pc3.green(` \u2713 App launched on ${simulatorName}
1526
+ execSync3(`xcrun simctl launch booted "${bundleId}"`, { stdio: "pipe" });
1527
+ console.log(pc4.green(` \u2713 App launched on ${simulatorName}
1166
1528
  `));
1167
1529
  } catch (err) {
1168
- console.error(pc3.red(` \u2717 Failed to launch app: ${err.message}`));
1530
+ console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
1169
1531
  process.exit(1);
1170
1532
  }
1171
1533
  } 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"));
1534
+ console.log(pc4.yellow(" Could not locate .app bundle in DerivedData."));
1535
+ console.log(pc4.dim(" Try running the app from Xcode directly.\n"));
1174
1536
  }
1175
1537
  });
1176
1538
  }
1177
1539
  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"));
1540
+ const androidDir = join4(cwd, "android");
1541
+ if (!existsSync4(androidDir)) {
1542
+ console.log(pc4.yellow(" No android/ directory found."));
1543
+ console.log(pc4.dim(" To add Android support, create an Android project in the android/ directory."));
1544
+ console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1183
1545
  return;
1184
1546
  }
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"));
1547
+ const gradlew = join4(androidDir, "gradlew");
1548
+ if (!existsSync4(gradlew)) {
1549
+ console.error(pc4.red(" \u2717 gradlew not found in android/ directory"));
1550
+ console.log(pc4.dim(" Make sure your Android project has the Gradle wrapper.\n"));
1189
1551
  process.exit(1);
1190
1552
  }
1191
- console.log(pc3.white(" Building Android app with Gradle..."));
1192
- const gradle = spawn2(
1553
+ console.log(pc4.white(" Building Android app with Gradle..."));
1554
+ const gradle = spawn3(
1193
1555
  "./gradlew",
1194
1556
  ["assembleDebug"],
1195
1557
  {
@@ -1209,56 +1571,283 @@ function runAndroid(cwd, options) {
1209
1571
  gradle.stdout?.on("data", (data) => {
1210
1572
  const text = data.toString().trim();
1211
1573
  if (text) {
1212
- console.log(pc3.dim(` ${text}`));
1574
+ console.log(pc4.dim(` ${text}`));
1213
1575
  }
1214
1576
  });
1215
1577
  gradle.stderr?.on("data", (data) => {
1216
1578
  const text = data.toString().trim();
1217
1579
  if (text.includes("ERROR") || text.includes("FAILURE")) {
1218
- console.log(pc3.red(` ${text}`));
1580
+ console.log(pc4.red(` ${text}`));
1219
1581
  }
1220
1582
  });
1221
1583
  gradle.on("error", (err) => {
1222
- console.error(pc3.red(` Gradle process error: ${err.message}`));
1584
+ console.error(pc4.red(` Gradle process error: ${err.message}`));
1223
1585
  cleanupGradle();
1224
1586
  });
1225
1587
  gradle.on("close", (code) => {
1226
1588
  if (code !== 0) {
1227
- console.error(pc3.red(` \u2717 Gradle build failed (exit code ${code})`));
1589
+ console.error(pc4.red(` \u2717 Gradle build failed (exit code ${code})`));
1228
1590
  process.exit(1);
1229
1591
  }
1230
- console.log(pc3.green(" \u2713 Build successful\n"));
1592
+ console.log(pc4.green(" \u2713 Build successful\n"));
1231
1593
  const apkPath = findApkPath(androidDir);
1232
1594
  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"));
1595
+ console.log(pc4.yellow(" Could not locate debug APK."));
1596
+ console.log(pc4.dim(" Expected at android/app/build/outputs/apk/debug/\n"));
1235
1597
  return;
1236
1598
  }
1237
- console.log(pc3.white(" Installing APK on device/emulator..."));
1599
+ console.log(pc4.white(" Installing APK on device/emulator..."));
1238
1600
  try {
1239
- execSync2(`adb install -r "${apkPath}"`, { stdio: "pipe" });
1240
- console.log(pc3.green(" \u2713 APK installed"));
1601
+ execSync3(`adb install -r "${apkPath}"`, { stdio: "pipe" });
1602
+ console.log(pc4.green(" \u2713 APK installed"));
1241
1603
  } 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"));
1604
+ console.error(pc4.red(` \u2717 Failed to install APK: ${err.message}`));
1605
+ console.log(pc4.dim(" Make sure an emulator is running or a device is connected (adb devices).\n"));
1244
1606
  process.exit(1);
1245
1607
  }
1246
1608
  const componentName = `${options.package}/${options.activity}`;
1247
- console.log(pc3.white(` Launching ${componentName}...`));
1609
+ console.log(pc4.white(` Launching ${componentName}...`));
1248
1610
  try {
1249
- execSync2(`adb shell am start -n "${componentName}"`, { stdio: "pipe" });
1250
- console.log(pc3.green(` \u2713 App launched
1611
+ execSync3(`adb shell am start -n "${componentName}"`, { stdio: "pipe" });
1612
+ console.log(pc4.green(` \u2713 App launched
1251
1613
  `));
1252
1614
  } catch (err) {
1253
- console.error(pc3.red(` \u2717 Failed to launch app: ${err.message}`));
1615
+ console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
1616
+ process.exit(1);
1617
+ }
1618
+ });
1619
+ }
1620
+ function runMacOS(cwd, options) {
1621
+ const macosDir = join4(cwd, "macos");
1622
+ if (!existsSync4(macosDir)) {
1623
+ console.log(pc4.yellow(" No macos/ directory found."));
1624
+ console.log(pc4.dim(" To add macOS support, create an Xcode project in the macos/ directory."));
1625
+ console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
1626
+ return;
1627
+ }
1628
+ let xcodeProject = null;
1629
+ for (const ext of [".xcworkspace", ".xcodeproj"]) {
1630
+ try {
1631
+ const entries = readdirSync2(macosDir);
1632
+ const match = entries.find((e) => e.endsWith(ext));
1633
+ if (match) {
1634
+ xcodeProject = join4(macosDir, match);
1635
+ break;
1636
+ }
1637
+ } catch {
1638
+ }
1639
+ }
1640
+ if (!xcodeProject) {
1641
+ console.log(pc4.yellow(" No Xcode project found in ./macos/"));
1642
+ return;
1643
+ }
1644
+ const isWorkspace = xcodeProject.endsWith(".xcworkspace");
1645
+ const scheme = options.scheme || xcodeProject.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
1646
+ const projectFlag = isWorkspace ? "-workspace" : "-project";
1647
+ console.log(pc4.white(` Building ${scheme} for macOS...`));
1648
+ const xcodebuild = spawn3(
1649
+ "xcodebuild",
1650
+ [projectFlag, xcodeProject, "-scheme", scheme, "-destination", "platform=macOS", "build"],
1651
+ {
1652
+ cwd,
1653
+ stdio: "pipe",
1654
+ env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
1655
+ }
1656
+ );
1657
+ xcodebuild.stderr?.on("data", (data) => {
1658
+ const text = data.toString().trim();
1659
+ if (text.includes("error:") || text.includes("warning:")) {
1660
+ console.log(pc4.dim(` ${text}`));
1661
+ }
1662
+ });
1663
+ xcodebuild.on("close", (code) => {
1664
+ if (code !== 0) {
1665
+ console.error(pc4.red(` \u2717 Build failed (exit code ${code})`));
1254
1666
  process.exit(1);
1255
1667
  }
1668
+ console.log(pc4.green(" \u2713 Build successful\n"));
1669
+ const derivedDataBase = join4(process.env.HOME || "~", "Library/Developer/Xcode/DerivedData");
1670
+ let appPath = null;
1671
+ if (existsSync4(derivedDataBase)) {
1672
+ try {
1673
+ const projects = readdirSync2(derivedDataBase);
1674
+ for (const project of projects.reverse()) {
1675
+ const productsDir = join4(derivedDataBase, project, "Build/Products/Debug");
1676
+ if (existsSync4(productsDir)) {
1677
+ const entries = readdirSync2(productsDir);
1678
+ const app = entries.find((e) => e.endsWith(".app"));
1679
+ if (app) {
1680
+ appPath = join4(productsDir, app);
1681
+ break;
1682
+ }
1683
+ }
1684
+ }
1685
+ } catch {
1686
+ }
1687
+ }
1688
+ if (appPath) {
1689
+ console.log(pc4.white(` Launching ${appPath}...`));
1690
+ try {
1691
+ execSync3(`open "${appPath}"`, { stdio: "pipe" });
1692
+ console.log(pc4.green(` \u2713 App launched
1693
+ `));
1694
+ } catch (err) {
1695
+ console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
1696
+ }
1697
+ } else {
1698
+ console.log(pc4.yellow(" Could not locate .app bundle in DerivedData."));
1699
+ console.log(pc4.dim(" Try running the app from Xcode directly.\n"));
1700
+ }
1256
1701
  });
1257
1702
  }
1258
1703
 
1704
+ // src/commands/generate.ts
1705
+ import { Command as Command5 } from "commander";
1706
+ import { existsSync as existsSync5 } from "fs";
1707
+ import { join as join5 } from "path";
1708
+ import pc5 from "picocolors";
1709
+ import { parseDirectory } from "@thelacanians/vue-native-sfc-parser";
1710
+ import { generateCode, writeGeneratedFiles, cleanGeneratedFiles, validateNativeBlocks, formatValidationErrors } from "@thelacanians/vue-native-codegen";
1711
+ var generateCommand = new Command5("generate").description("Generate native code from <native> blocks in Vue SFC files").option("--root <path>", "project root directory", process.cwd()).option("--watch", "watch mode - regenerate on file changes").option("--clean", "clean generated files before generating").option("--ios-output <path>", "iOS Swift output directory").option("--android-output <path>", "Android Kotlin output directory").option("--macos-output <path>", "macOS Swift output directory").option("--ts-output <path>", "TypeScript output directory").option("--no-swift", "disable Swift generation").option("--no-kotlin", "disable Kotlin generation").option("--no-typescript", "disable TypeScript generation").option("--exclude <patterns>", "patterns to exclude (comma-separated)").action(async (options) => {
1712
+ const cwd = options.root;
1713
+ const appDir = join5(cwd, "app");
1714
+ if (!existsSync5(appDir)) {
1715
+ console.error(pc5.red(`Error: App directory not found at ${appDir}`));
1716
+ console.error(pc5.yellow("Make sure you run this command from your project root."));
1717
+ process.exit(1);
1718
+ }
1719
+ const codegenOptions = {
1720
+ root: cwd,
1721
+ includeHeader: true,
1722
+ generateSwift: options.swift !== false,
1723
+ generateKotlin: options.kotlin !== false,
1724
+ generateTypeScript: options.typescript !== false
1725
+ };
1726
+ if (options.iosOutput) codegenOptions.iosOutputDir = options.iosOutput;
1727
+ if (options.androidOutput) codegenOptions.androidOutputDir = options.androidOutput;
1728
+ if (options.macosOutput) codegenOptions.macosOutputDir = options.macosOutput;
1729
+ if (options.tsOutput) codegenOptions.typescriptOutputDir = options.tsOutput;
1730
+ const exclude = options.exclude ? options.exclude.split(",").map((p) => p.trim()) : ["node_modules", "dist", ".git", ".turbo"];
1731
+ async function runGeneration() {
1732
+ console.log(pc5.cyan("\n\u{1F527} Vue Native Code Generator"));
1733
+ console.log(pc5.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
1734
+ try {
1735
+ if (options.clean) {
1736
+ console.log(pc5.yellow("\u{1F5D1}\uFE0F Cleaning generated files..."));
1737
+ cleanGeneratedFiles(codegenOptions, cwd);
1738
+ console.log(pc5.green("\u2713 Cleaned\n"));
1739
+ }
1740
+ console.log(pc5.blue("\u{1F4C4} Scanning SFC files..."));
1741
+ const parseResult = parseDirectory(".", {
1742
+ root: cwd,
1743
+ exclude
1744
+ });
1745
+ if (parseResult.errors.length > 0) {
1746
+ console.log(pc5.yellow("\u26A0\uFE0F Parse warnings:"));
1747
+ parseResult.errors.forEach((err) => {
1748
+ console.log(pc5.dim(` - ${err.file}:${err.line || "?"} ${err.message}`));
1749
+ });
1750
+ console.log();
1751
+ }
1752
+ const blockCount = parseResult.allNativeBlocks.length;
1753
+ console.log(pc5.green(`\u2713 Found ${blockCount} <native> block${blockCount !== 1 ? "s" : ""}`));
1754
+ if (blockCount === 0) {
1755
+ console.log(pc5.yellow("\n\u26A0\uFE0F No <native> blocks found."));
1756
+ console.log(pc5.dim('Add <native platform="ios"> or <native platform="android"> blocks to your SFC files.'));
1757
+ return;
1758
+ }
1759
+ console.log(pc5.blue("\u{1F50D} Validating native code..."));
1760
+ const validation = validateNativeBlocks(parseResult.allNativeBlocks);
1761
+ if (!validation.isValid) {
1762
+ console.log(pc5.red("\n\u274C Validation failed:"));
1763
+ console.log(formatValidationErrors(validation));
1764
+ process.exit(1);
1765
+ }
1766
+ if (validation.warnings.length > 0) {
1767
+ console.log(pc5.yellow(`\u26A0\uFE0F ${validation.warnings.length} warning${validation.warnings.length !== 1 ? "s" : ""}`));
1768
+ } else {
1769
+ console.log(pc5.green("\u2713 Validation passed"));
1770
+ }
1771
+ console.log();
1772
+ const iosBlocks = parseResult.allNativeBlocks.filter((b) => b.platform === "ios" || b.platform === "macos");
1773
+ const androidBlocks = parseResult.allNativeBlocks.filter((b) => b.platform === "android");
1774
+ if (iosBlocks.length > 0) {
1775
+ console.log(pc5.dim(` - iOS/macOS: ${iosBlocks.length} block${iosBlocks.length !== 1 ? "s" : ""}`));
1776
+ }
1777
+ if (androidBlocks.length > 0) {
1778
+ console.log(pc5.dim(` - Android: ${androidBlocks.length} block${androidBlocks.length !== 1 ? "s" : ""}`));
1779
+ }
1780
+ console.log();
1781
+ console.log(pc5.blue("\u2699\uFE0F Generating code..."));
1782
+ const codegenResult = generateCode(parseResult.allNativeBlocks, codegenOptions);
1783
+ if (codegenResult.errors.length > 0) {
1784
+ console.log(pc5.red("\n\u274C Generation errors:"));
1785
+ codegenResult.errors.forEach((err) => {
1786
+ console.log(pc5.red(` - ${err.file} ${err.message}`));
1787
+ });
1788
+ process.exit(1);
1789
+ }
1790
+ console.log(pc5.blue("\u{1F4DD} Writing files..."));
1791
+ const writeResult = writeGeneratedFiles(codegenResult, cwd);
1792
+ if (writeResult.errors.length > 0) {
1793
+ console.log(pc5.red("\n\u274C Write errors:"));
1794
+ writeResult.errors.forEach((err) => {
1795
+ console.log(pc5.red(` - ${err.message}`));
1796
+ });
1797
+ process.exit(1);
1798
+ }
1799
+ console.log(pc5.green("\n\u2705 Generation complete!\n"));
1800
+ console.log(pc5.dim("Generated files:"));
1801
+ console.log(pc5.dim(` - Swift: ${codegenResult.stats.swiftFiles} file${codegenResult.stats.swiftFiles !== 1 ? "s" : ""}`));
1802
+ console.log(pc5.dim(` - Kotlin: ${codegenResult.stats.kotlinFiles} file${codegenResult.stats.kotlinFiles !== 1 ? "s" : ""}`));
1803
+ console.log(pc5.dim(` - TypeScript: ${codegenResult.stats.typescriptFiles} file${codegenResult.stats.typescriptFiles !== 1 ? "s" : ""}`));
1804
+ console.log();
1805
+ if (codegenResult.warnings.length > 0) {
1806
+ console.log(pc5.yellow("\u26A0\uFE0F Warnings:"));
1807
+ codegenResult.warnings.forEach((warn) => {
1808
+ console.log(pc5.dim(` - ${warn.message}`));
1809
+ });
1810
+ console.log();
1811
+ }
1812
+ console.log(pc5.green("\u{1F389} Ready to build!\n"));
1813
+ } catch (error) {
1814
+ console.error(pc5.red("\n\u274C Fatal error:"));
1815
+ console.error(pc5.red(error.message));
1816
+ console.error(pc5.dim(error.stack || ""));
1817
+ process.exit(1);
1818
+ }
1819
+ }
1820
+ await runGeneration();
1821
+ if (options.watch) {
1822
+ console.log(pc5.cyan("\n\u{1F441}\uFE0F Watch mode enabled. Press Ctrl+C to stop.\n"));
1823
+ const chokidar = await import("chokidar");
1824
+ const watcher = chokidar.default.watch("app/**/*.vue", {
1825
+ cwd,
1826
+ ignored: exclude,
1827
+ ignoreInitial: true
1828
+ });
1829
+ watcher.on("change", async (file) => {
1830
+ console.log(pc5.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`) + pc5.blue(` Changed: ${file}`));
1831
+ await runGeneration();
1832
+ });
1833
+ watcher.on("error", (error) => {
1834
+ console.error(pc5.red("Watch error:"), error);
1835
+ });
1836
+ process.on("SIGINT", () => {
1837
+ console.log(pc5.yellow("\n\u23F9\uFE0F Stopping watch mode..."));
1838
+ watcher.close();
1839
+ process.exit(0);
1840
+ });
1841
+ await new Promise(() => {
1842
+ });
1843
+ }
1844
+ });
1845
+
1259
1846
  // src/cli.ts
1260
1847
  program.name("vue-native").description("Vue Native \u2014 build native iOS and Android apps with Vue.js").version("0.1.0");
1848
+ program.addCommand(buildCommand);
1261
1849
  program.addCommand(createCommand);
1262
1850
  program.addCommand(devCommand);
1263
1851
  program.addCommand(runCommand);
1852
+ program.addCommand(generateCommand);
1264
1853
  program.parse(process.argv);