@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.
- package/dist/cli.js +789 -200
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +156 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +33 -13
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +27 -6
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +9 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/EventThrottle.kt +57 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
- package/native/android/gradle.properties +1 -0
- package/native/android/gradlew +1 -1
- package/native/ios/VueNativeCore/Package.swift +1 -1
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +80 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +244 -112
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +19 -2
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +9 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +8 -3
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
- package/native/macos/VueNativeMacOS/Package.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
- package/native/shared/VueNativeShared/AGENTS.md +129 -0
- package/native/shared/VueNativeShared/Package.swift +14 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
- package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
- 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/
|
|
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
|
|
335
|
+
import { existsSync as existsSync2 } from "fs";
|
|
336
|
+
import pc2 from "picocolors";
|
|
13
337
|
var VERSION = "0.4.14";
|
|
14
|
-
var
|
|
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(
|
|
344
|
+
console.error(pc2.red(`Invalid template "${template}". Choose: blank, tabs, drawer`));
|
|
18
345
|
process.exit(1);
|
|
19
346
|
}
|
|
20
|
-
const dir =
|
|
21
|
-
console.log(
|
|
22
|
-
Creating Vue Native project: ${
|
|
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(
|
|
27
|
-
await mkdir(
|
|
28
|
-
await writeFile(
|
|
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":
|
|
40
|
-
"@thelacanians/vue-native-navigation":
|
|
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":
|
|
45
|
-
"@vitejs/plugin-vue":
|
|
46
|
-
"vite":
|
|
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(
|
|
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(
|
|
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 =
|
|
75
|
-
const iosSrcDir =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
229
|
-
const androidAppDir =
|
|
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 =
|
|
233
|
-
const androidKotlinDir =
|
|
234
|
-
const androidResValuesDir =
|
|
235
|
-
const androidResXmlDir =
|
|
236
|
-
const androidDebugResXmlDir =
|
|
237
|
-
const androidGradleWrapperDir =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
459
|
-
if (
|
|
460
|
-
const nativeDir =
|
|
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(
|
|
463
|
-
}
|
|
464
|
-
console.log(
|
|
465
|
-
console.log(
|
|
466
|
-
console.log(
|
|
467
|
-
console.log(
|
|
468
|
-
console.log(
|
|
469
|
-
console.log(
|
|
470
|
-
console.log(
|
|
471
|
-
console.log(
|
|
472
|
-
console.log(
|
|
473
|
-
console.log(
|
|
474
|
-
console.log(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
817
|
-
import { join as
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1177
|
+
execSync2(`xcrun simctl boot "${udid}"`, { stdio: "pipe" });
|
|
851
1178
|
} catch {
|
|
852
1179
|
}
|
|
853
1180
|
try {
|
|
854
|
-
|
|
1181
|
+
execSync2("open -a Simulator", { stdio: "pipe" });
|
|
855
1182
|
} catch {
|
|
856
1183
|
}
|
|
857
1184
|
}
|
|
858
1185
|
function detectAndroidEmulators() {
|
|
859
1186
|
try {
|
|
860
|
-
const output =
|
|
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
|
|
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 =
|
|
871
|
-
console.log(
|
|
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(
|
|
1200
|
+
console.log(pc3.white(" Detecting iOS Simulators..."));
|
|
874
1201
|
const simulators = detectIOSSimulators();
|
|
875
1202
|
if (simulators.length === 0) {
|
|
876
|
-
console.log(
|
|
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(
|
|
1214
|
+
console.log(pc3.white(` Booting ${target.name}...`));
|
|
888
1215
|
bootSimulator(target.udid);
|
|
889
1216
|
}
|
|
890
|
-
console.log(
|
|
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(
|
|
1223
|
+
console.log(pc3.white(" Detecting Android emulators..."));
|
|
897
1224
|
const emulators = detectAndroidEmulators();
|
|
898
1225
|
if (emulators.length === 0) {
|
|
899
|
-
console.log(
|
|
1226
|
+
console.log(pc3.yellow(" No Android emulators detected. Start one via Android Studio or `emulator -avd <name>`."));
|
|
900
1227
|
} else {
|
|
901
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1287
|
+
console.error(pc3.red(`WebSocket server error: ${err.message}`));
|
|
938
1288
|
});
|
|
939
|
-
console.log(
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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 (
|
|
946
|
-
console.log(
|
|
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(
|
|
949
|
-
console.log(
|
|
950
|
-
const vite =
|
|
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(
|
|
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(
|
|
1320
|
+
if (text) console.log(pc3.yellow(` [vite] ${text}`));
|
|
962
1321
|
});
|
|
963
1322
|
vite.on("error", (err) => {
|
|
964
|
-
console.error(
|
|
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(
|
|
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(
|
|
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
|
|
1005
|
-
import { spawn as
|
|
1006
|
-
import { existsSync as
|
|
1007
|
-
import { join as
|
|
1008
|
-
import
|
|
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 =
|
|
1369
|
+
const derivedDataBase = join4(
|
|
1011
1370
|
process.env.HOME || "~",
|
|
1012
1371
|
"Library/Developer/Xcode/DerivedData"
|
|
1013
1372
|
);
|
|
1014
|
-
if (
|
|
1373
|
+
if (existsSync4(derivedDataBase)) {
|
|
1015
1374
|
try {
|
|
1016
|
-
const projects =
|
|
1375
|
+
const projects = readdirSync2(derivedDataBase);
|
|
1017
1376
|
for (const project of projects.reverse()) {
|
|
1018
|
-
const productsDir =
|
|
1377
|
+
const productsDir = join4(
|
|
1019
1378
|
derivedDataBase,
|
|
1020
1379
|
project,
|
|
1021
1380
|
"Build/Products/Debug-iphonesimulator"
|
|
1022
1381
|
);
|
|
1023
|
-
if (
|
|
1024
|
-
const entries =
|
|
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
|
|
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 =
|
|
1038
|
-
if (
|
|
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 =
|
|
1054
|
-
if (
|
|
1412
|
+
const apkDir = join4(androidDir, "app", "build", "outputs", "apk", "debug");
|
|
1413
|
+
if (existsSync4(apkDir)) {
|
|
1055
1414
|
try {
|
|
1056
|
-
const entries =
|
|
1415
|
+
const entries = readdirSync2(apkDir);
|
|
1057
1416
|
const apk = entries.find((e) => e.endsWith(".apk") && !e.includes("androidTest"));
|
|
1058
1417
|
if (apk) {
|
|
1059
|
-
return
|
|
1418
|
+
return join4(apkDir, apk);
|
|
1060
1419
|
}
|
|
1061
1420
|
} catch {
|
|
1062
1421
|
}
|
|
1063
1422
|
}
|
|
1064
1423
|
return null;
|
|
1065
1424
|
}
|
|
1066
|
-
var runCommand = new
|
|
1067
|
-
if (platform !== "ios" && platform !== "android") {
|
|
1068
|
-
console.error(
|
|
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
|
-
|
|
1073
|
-
|
|
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(
|
|
1435
|
+
console.log(pc4.white(" Building JS bundle..."));
|
|
1076
1436
|
try {
|
|
1077
|
-
|
|
1078
|
-
console.log(
|
|
1437
|
+
execSync3("bun run vite build", { cwd, stdio: "inherit" });
|
|
1438
|
+
console.log(pc4.green(" \u2713 Bundle built\n"));
|
|
1079
1439
|
} catch {
|
|
1080
|
-
console.error(
|
|
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 =
|
|
1092
|
-
if (
|
|
1453
|
+
const iosDir = join4(cwd, "ios");
|
|
1454
|
+
if (existsSync4(iosDir)) {
|
|
1093
1455
|
for (const ext of [".xcworkspace", ".xcodeproj"]) {
|
|
1094
1456
|
try {
|
|
1095
|
-
const entries =
|
|
1457
|
+
const entries = readdirSync2(iosDir);
|
|
1096
1458
|
const match = entries.find((e) => e.endsWith(ext));
|
|
1097
1459
|
if (match) {
|
|
1098
|
-
xcodeProject =
|
|
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(
|
|
1107
|
-
console.log(
|
|
1108
|
-
console.log(
|
|
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(
|
|
1116
|
-
const xcodebuild =
|
|
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(
|
|
1490
|
+
console.log(pc4.dim(` ${text}`));
|
|
1129
1491
|
}
|
|
1130
1492
|
});
|
|
1131
1493
|
xcodebuild.on("close", (code) => {
|
|
1132
1494
|
if (code !== 0) {
|
|
1133
|
-
console.error(
|
|
1495
|
+
console.error(pc4.red(` \u2717 Build failed (exit code ${code})`));
|
|
1134
1496
|
process.exit(1);
|
|
1135
1497
|
}
|
|
1136
|
-
console.log(
|
|
1498
|
+
console.log(pc4.green(" \u2713 Build successful\n"));
|
|
1137
1499
|
if (options.device) {
|
|
1138
|
-
console.log(
|
|
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(
|
|
1143
|
-
console.log(
|
|
1504
|
+
const bundleId = options.bundleId || readBundleId(join4(cwd, "ios"));
|
|
1505
|
+
console.log(pc4.white(` Booting simulator "${simulatorName}"...`));
|
|
1144
1506
|
try {
|
|
1145
|
-
|
|
1507
|
+
execSync3(`xcrun simctl boot "${simulatorName}"`, { stdio: "pipe" });
|
|
1146
1508
|
} catch {
|
|
1147
1509
|
}
|
|
1148
1510
|
try {
|
|
1149
|
-
|
|
1511
|
+
execSync3("open -a Simulator", { stdio: "pipe" });
|
|
1150
1512
|
} catch {
|
|
1151
1513
|
}
|
|
1152
|
-
const appPath = findAppPath(
|
|
1514
|
+
const appPath = findAppPath(join4(cwd, "ios"));
|
|
1153
1515
|
if (appPath) {
|
|
1154
|
-
console.log(
|
|
1516
|
+
console.log(pc4.white(` Installing app on simulator...`));
|
|
1155
1517
|
try {
|
|
1156
|
-
|
|
1157
|
-
console.log(
|
|
1518
|
+
execSync3(`xcrun simctl install booted "${appPath}"`, { stdio: "pipe" });
|
|
1519
|
+
console.log(pc4.green(" \u2713 App installed"));
|
|
1158
1520
|
} catch (err) {
|
|
1159
|
-
console.error(
|
|
1521
|
+
console.error(pc4.red(` \u2717 Failed to install app: ${err.message}`));
|
|
1160
1522
|
process.exit(1);
|
|
1161
1523
|
}
|
|
1162
|
-
console.log(
|
|
1524
|
+
console.log(pc4.white(` Launching ${bundleId}...`));
|
|
1163
1525
|
try {
|
|
1164
|
-
|
|
1165
|
-
console.log(
|
|
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(
|
|
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(
|
|
1173
|
-
console.log(
|
|
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 =
|
|
1179
|
-
if (!
|
|
1180
|
-
console.log(
|
|
1181
|
-
console.log(
|
|
1182
|
-
console.log(
|
|
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 =
|
|
1186
|
-
if (!
|
|
1187
|
-
console.error(
|
|
1188
|
-
console.log(
|
|
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(
|
|
1192
|
-
const gradle =
|
|
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(
|
|
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(
|
|
1580
|
+
console.log(pc4.red(` ${text}`));
|
|
1219
1581
|
}
|
|
1220
1582
|
});
|
|
1221
1583
|
gradle.on("error", (err) => {
|
|
1222
|
-
console.error(
|
|
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(
|
|
1589
|
+
console.error(pc4.red(` \u2717 Gradle build failed (exit code ${code})`));
|
|
1228
1590
|
process.exit(1);
|
|
1229
1591
|
}
|
|
1230
|
-
console.log(
|
|
1592
|
+
console.log(pc4.green(" \u2713 Build successful\n"));
|
|
1231
1593
|
const apkPath = findApkPath(androidDir);
|
|
1232
1594
|
if (!apkPath) {
|
|
1233
|
-
console.log(
|
|
1234
|
-
console.log(
|
|
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(
|
|
1599
|
+
console.log(pc4.white(" Installing APK on device/emulator..."));
|
|
1238
1600
|
try {
|
|
1239
|
-
|
|
1240
|
-
console.log(
|
|
1601
|
+
execSync3(`adb install -r "${apkPath}"`, { stdio: "pipe" });
|
|
1602
|
+
console.log(pc4.green(" \u2713 APK installed"));
|
|
1241
1603
|
} catch (err) {
|
|
1242
|
-
console.error(
|
|
1243
|
-
console.log(
|
|
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(
|
|
1609
|
+
console.log(pc4.white(` Launching ${componentName}...`));
|
|
1248
1610
|
try {
|
|
1249
|
-
|
|
1250
|
-
console.log(
|
|
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(
|
|
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);
|