@thelacanians/vue-native-cli 0.4.15 → 0.6.2

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 (116) hide show
  1. package/dist/cli.js +329 -15
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  6. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  7. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  8. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  9. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  13. package/native/android/gradle.properties +1 -0
  14. package/native/android/gradlew +1 -1
  15. package/native/ios/VueNativeCore/Package.swift +1 -1
  16. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
  17. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
  18. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  19. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  23. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  24. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  25. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  26. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  27. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  28. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  29. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  30. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  31. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  32. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  33. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  34. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  35. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  36. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  94. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  95. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  96. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  97. package/native/shared/VueNativeShared/Package.swift +14 -0
  98. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  99. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  100. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  101. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  102. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  103. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  104. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  115. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  116. package/package.json +8 -2
package/dist/cli.js CHANGED
@@ -59,15 +59,16 @@ function ensureOutputDir(outputPath) {
59
59
  mkdirSync(outputPath, { recursive: true });
60
60
  }
61
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"'));
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
65
  process.exit(1);
66
66
  }
67
67
  const cwd = process.cwd();
68
68
  const outputDir = join(cwd, options.output);
69
+ const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
69
70
  console.log(pc.cyan(`
70
- Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${platform === "ios" ? "iOS" : "Android"})
71
+ Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${platformLabel})
71
72
  `));
72
73
  console.log(pc.white(" Building JS bundle for production..."));
73
74
  try {
@@ -79,8 +80,10 @@ var buildCommand = new Command("build").description("Create a release build of t
79
80
  }
80
81
  if (platform === "ios") {
81
82
  buildIOS(cwd, outputDir, options);
82
- } else {
83
+ } else if (platform === "android") {
83
84
  buildAndroid(cwd, outputDir, options);
85
+ } else {
86
+ buildMacOS(cwd, outputDir, options);
84
87
  }
85
88
  });
86
89
  function buildIOS(cwd, outputDir, options) {
@@ -244,6 +247,85 @@ function buildAndroid(cwd, outputDir, options) {
244
247
  }
245
248
  });
246
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
+ }
247
329
 
248
330
  // src/commands/create.ts
249
331
  import { Command as Command2 } from "commander";
@@ -253,6 +335,9 @@ import { fileURLToPath } from "url";
253
335
  import { existsSync as existsSync2 } from "fs";
254
336
  import pc2 from "picocolors";
255
337
  var VERSION = "0.4.14";
338
+ var JS_PACKAGE_VERSION = "^0.5.0";
339
+ var VITE_PLUGIN_VUE_VERSION = "^6.0.5";
340
+ var VITE_VERSION = "^8.0.0";
256
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) => {
257
342
  const template = options.template;
258
343
  if (!["blank", "tabs", "drawer"].includes(template)) {
@@ -278,14 +363,14 @@ Creating Vue Native project: ${pc2.bold(name)} (template: ${template})
278
363
  typecheck: "tsc --noEmit"
279
364
  },
280
365
  dependencies: {
281
- "@thelacanians/vue-native-runtime": "^0.4.0",
282
- "@thelacanians/vue-native-navigation": "^0.4.0",
366
+ "@thelacanians/vue-native-runtime": JS_PACKAGE_VERSION,
367
+ "@thelacanians/vue-native-navigation": JS_PACKAGE_VERSION,
283
368
  "vue": "^3.5.0"
284
369
  },
285
370
  devDependencies: {
286
- "@thelacanians/vue-native-vite-plugin": "^0.4.0",
287
- "@vitejs/plugin-vue": "^5.0.0",
288
- "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,
289
374
  "typescript": "^5.7.0"
290
375
  }
291
376
  }, null, 2));
@@ -1337,14 +1422,15 @@ function findApkPath(androidDir) {
1337
1422
  }
1338
1423
  return null;
1339
1424
  }
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) => {
1341
- if (platform !== "ios" && platform !== "android") {
1342
- console.error(pc4.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"'));
1343
1428
  process.exit(1);
1344
1429
  }
1345
1430
  const cwd = process.cwd();
1431
+ const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
1346
1432
  console.log(pc4.cyan(`
1347
- \u{1F4F1} Vue Native \u2014 Run ${platform === "ios" ? "iOS" : "Android"}
1433
+ \u{1F4F1} Vue Native \u2014 Run ${platformLabel}
1348
1434
  `));
1349
1435
  console.log(pc4.white(" Building JS bundle..."));
1350
1436
  try {
@@ -1356,8 +1442,10 @@ var runCommand = new Command4("run").description("Build and run the app").argume
1356
1442
  }
1357
1443
  if (platform === "ios") {
1358
1444
  runIOS(cwd, options);
1359
- } else {
1445
+ } else if (platform === "android") {
1360
1446
  runAndroid(cwd, options);
1447
+ } else {
1448
+ runMacOS(cwd, options);
1361
1449
  }
1362
1450
  });
1363
1451
  function runIOS(cwd, options) {
@@ -1529,6 +1617,231 @@ function runAndroid(cwd, options) {
1529
1617
  }
1530
1618
  });
1531
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})`));
1666
+ process.exit(1);
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
+ }
1701
+ });
1702
+ }
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
+ });
1532
1845
 
1533
1846
  // src/cli.ts
1534
1847
  program.name("vue-native").description("Vue Native \u2014 build native iOS and Android apps with Vue.js").version("0.1.0");
@@ -1536,4 +1849,5 @@ program.addCommand(buildCommand);
1536
1849
  program.addCommand(createCommand);
1537
1850
  program.addCommand(devCommand);
1538
1851
  program.addCommand(runCommand);
1852
+ program.addCommand(generateCommand);
1539
1853
  program.parse(process.argv);
@@ -6,6 +6,7 @@ import android.os.Looper
6
6
  import android.util.Log
7
7
  import android.view.View
8
8
  import android.view.ViewGroup
9
+ import android.widget.FrameLayout
9
10
  import org.json.JSONArray
10
11
  import org.json.JSONObject
11
12
 
@@ -67,6 +68,26 @@ class NativeBridge(private val context: Context) {
67
68
  /** The container view from the host Activity — where the root view is attached. */
68
69
  var hostContainer: ViewGroup? = null
69
70
 
71
+ // -- Teleport support --
72
+ /** Maps teleport marker IDs for cleanup */
73
+ private val teleportMarkers = mutableMapOf<Int, Pair<Int, Int>>()
74
+
75
+ /** Maps parent node IDs to their teleport containers */
76
+ private val teleportContainers = mutableMapOf<Int, ViewGroup>()
77
+
78
+ /** Modal container for teleporting modals */
79
+ private val modalContainer: FrameLayout by lazy {
80
+ FrameLayout(context).apply {
81
+ layoutParams = FrameLayout.LayoutParams(
82
+ FrameLayout.LayoutParams.MATCH_PARENT,
83
+ FrameLayout.LayoutParams.MATCH_PARENT
84
+ )
85
+ setBackgroundColor(android.graphics.Color.TRANSPARENT)
86
+ isClickable = true
87
+ isFocusable = false
88
+ }
89
+ }
90
+
70
91
  /** Called when native fires an event to JS (nodeId, eventName, payloadJson). */
71
92
  var onFireEvent: ((nodeId: Int, eventName: String, payloadJson: String) -> Unit)? = null
72
93
 
@@ -143,6 +164,9 @@ class NativeBridge(private val context: Context) {
143
164
  "insertBefore" -> handleInsertBefore(args)
144
165
  "removeChild" -> handleRemoveChild(args)
145
166
  "setRootView" -> handleSetRootView(args)
167
+ "createTeleport" -> handleCreateTeleport(args)
168
+ "removeTeleport" -> handleRemoveTeleport(args)
169
+ "teleportTo" -> handleTeleportTo(args)
146
170
  "addEventListener" -> handleAddEventListener(args)
147
171
  "removeEventListener" -> handleRemoveEventListener(args)
148
172
  "invokeNativeModule" -> handleInvokeNativeModule(args)
@@ -311,6 +335,100 @@ class NativeBridge(private val context: Context) {
311
335
  ViewGroup.LayoutParams.MATCH_PARENT
312
336
  )
313
337
  )
338
+
339
+ // Add modal container for teleport
340
+ container.addView(
341
+ modalContainer,
342
+ ViewGroup.LayoutParams(
343
+ ViewGroup.LayoutParams.MATCH_PARENT,
344
+ ViewGroup.LayoutParams.MATCH_PARENT
345
+ )
346
+ )
347
+ }
348
+
349
+ // -- Teleport handlers --
350
+
351
+ private fun handleCreateTeleport(args: JSONArray) {
352
+ val parentId = args.getInt(0)
353
+ val startId = args.getInt(1)
354
+ val endId = args.getInt(2)
355
+
356
+ val parentView = nodeViews[parentId] ?: run {
357
+ Log.w(TAG, "Parent view not found for teleport (id: $parentId)")
358
+ return
359
+ }
360
+
361
+ // Store teleport marker IDs
362
+ teleportMarkers[parentId] = Pair(startId, endId)
363
+
364
+ // Create container for teleported content
365
+ val container = FrameLayout(context).apply {
366
+ id = View.generateViewId()
367
+ setBackgroundColor(android.graphics.Color.TRANSPARENT)
368
+ isClickable = true
369
+ isFocusable = false
370
+ }
371
+
372
+ if (parentView is ViewGroup) {
373
+ parentView.addView(container)
374
+ teleportContainers[parentId] = container
375
+ }
376
+
377
+ Log.d(TAG, "Created teleport container for parent $parentId")
378
+ }
379
+
380
+ private fun handleRemoveTeleport(args: JSONArray) {
381
+ val parentId = args.getInt(0)
382
+
383
+ // Remove teleport container
384
+ teleportContainers.remove(parentId)?.let { container ->
385
+ mainHandler.post {
386
+ (container.parent as? ViewGroup)?.removeView(container)
387
+ }
388
+ }
389
+
390
+ // Clean up markers
391
+ teleportMarkers.remove(parentId)
392
+
393
+ Log.d(TAG, "Removed teleport container for parent $parentId")
394
+ }
395
+
396
+ private fun handleTeleportTo(args: JSONArray) {
397
+ val target = args.getString(0)
398
+ val nodeId = args.getInt(1)
399
+
400
+ val targetView = getTeleportTarget(target) ?: run {
401
+ Log.w(TAG, "Teleport target '$target' not found")
402
+ return
403
+ }
404
+
405
+ val childView = nodeViews[nodeId] ?: run {
406
+ Log.w(TAG, "Node view not found for teleport (id: $nodeId)")
407
+ return
408
+ }
409
+
410
+ // Move view to teleport target
411
+ mainHandler.post {
412
+ (childView.parent as? ViewGroup)?.removeView(childView)
413
+ targetView.addView(childView)
414
+ childView.requestLayout()
415
+ }
416
+
417
+ Log.d(TAG, "Teleported node $nodeId to target '$target'")
418
+ }
419
+
420
+ private fun getTeleportTarget(target: String): ViewGroup? {
421
+ return when (target) {
422
+ "root" -> rootView as? ViewGroup
423
+ "modal" -> {
424
+ // Ensure modal container is added to root if not already
425
+ if (modalContainer.parent == null && rootView != null) {
426
+ (rootView as? ViewGroup)?.addView(modalContainer)
427
+ }
428
+ modalContainer
429
+ }
430
+ else -> null
431
+ }
314
432
  }
315
433
 
316
434
  private fun handleAddEventListener(args: JSONArray) {