catalyst-core 1.1.0 → 1.2.0-canary.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.
- package/bin/catalyst.js +54 -6
- package/changelog.md +27 -1
- package/dist/errors/registry.js +306 -18
- package/dist/errors-index.json +273 -70
- package/dist/native/androidProject/app/build.gradle.kts +53 -79
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CatalystConstants.kt +3 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +32 -4
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +5 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ConfigMappingTest.kt +25 -0
- package/dist/native/androidSetup.js +76 -170
- package/dist/native/androidToolchain.js +642 -0
- package/dist/native/appIcon.js +100 -0
- package/dist/native/buildAndroid/assets.js +29 -5
- package/dist/native/buildAndroid/build.js +110 -223
- package/dist/native/buildAndroid/config.js +57 -65
- package/dist/native/buildAndroid/devices.js +176 -0
- package/dist/native/buildAndroid/index.js +271 -46
- package/dist/native/buildAndroid/output.js +43 -0
- package/dist/native/buildAndroid/signing.js +456 -0
- package/dist/native/buildAppAndroid.js +27 -7
- package/dist/native/buildAppIos.js +139 -20
- package/dist/native/buildIos/assets.js +76 -4
- package/dist/native/buildIos/build.js +31 -181
- package/dist/native/buildIos/config.js +126 -11
- package/dist/native/buildIos/index.js +354 -34
- package/dist/native/buildIos/localhostCert.js +87 -0
- package/dist/native/buildIos/output.js +28 -0
- package/dist/native/buildIos/signing.js +596 -0
- package/dist/native/credentials.js +309 -0
- package/dist/native/exportCredentials.js +51 -0
- package/dist/native/iosToolchain.js +583 -0
- package/dist/native/iosnativeWebView/Configurations/Shared.xcconfig +1 -3
- package/dist/native/iosnativeWebView/iosnativeWebView/Info-Release.plist +2 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/PrivacyInfo.xcprivacy +40 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebView.entitlements +0 -2
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +4 -4
- package/dist/native/localState.js +148 -0
- package/dist/native/nativeConfig.js +621 -0
- package/dist/native/releaseOutput.js +53 -0
- package/dist/native/renameAndroidProject.js +154 -369
- package/dist/native/setupEmulatorIos.js +58 -275
- package/dist/native/setupServer.js +520 -124
- package/dist/native/utils.js +418 -115
- package/dist/vite/resolveDevServerConfig.js +21 -0
- package/mcp_v2/knowledge-base.json +13 -11
- package/mcp_v2/test/config.test.ts +40 -0
- package/mcp_v2/tools/build.js +80 -104
- package/mcp_v2/tools/config.js +181 -174
- package/package.json +1 -1
- package/test/cli/nativeFlags.test.ts +97 -0
- package/test/native/androidDevices.test.ts +230 -0
- package/test/native/androidElfAlignment.test.ts +148 -0
- package/test/native/androidSigning.test.ts +646 -0
- package/test/native/androidToolchain.test.ts +766 -0
- package/test/native/appIcon.test.ts +124 -0
- package/test/native/credentials.test.ts +241 -0
- package/test/native/interaction.test.ts +179 -0
- package/test/native/iosConfigPhase.test.ts +611 -0
- package/test/native/iosSigning.test.ts +1060 -0
- package/test/native/iosToolchain.test.ts +821 -0
- package/test/native/localState.test.ts +80 -0
- package/test/native/nativeConfig.test.ts +602 -0
- package/test/native/setupServer.test.ts +529 -0
- package/test/native/utils.test.ts +102 -127
- package/test/vite/resolveDevServerConfig.test.js +25 -0
- package/dist/native/androidProject/keystore/Untitled +0 -0
- package/dist/native/androidProject/keystore/release-key.jks +0 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/localhost.p12 +0 -0
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -8,7 +8,9 @@ import android.view.MotionEvent
|
|
|
8
8
|
import android.view.WindowManager
|
|
9
9
|
import androidx.activity.OnBackPressedCallback
|
|
10
10
|
import androidx.appcompat.app.AppCompatActivity
|
|
11
|
+
import androidx.core.graphics.Insets
|
|
11
12
|
import androidx.core.view.ViewCompat
|
|
13
|
+
import androidx.core.view.WindowInsetsCompat
|
|
12
14
|
|
|
13
15
|
import org.json.JSONObject
|
|
14
16
|
import java.util.Properties
|
|
@@ -51,6 +53,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
51
53
|
private var isHardwareAccelerationEnabled = false
|
|
52
54
|
private var currentUrl: String = ""
|
|
53
55
|
private var edgeToEdgeEnabled = false
|
|
56
|
+
private var backCallback: OnBackPressedCallback? = null
|
|
54
57
|
private var latestSafeAreaInsets: SafeAreaInsets = SafeAreaInsets.ZERO
|
|
55
58
|
private var splashOverlay: SplashOverlay? = null
|
|
56
59
|
|
|
@@ -77,6 +80,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
|
|
83
|
+
// targetSdk 35+ draws every window edge-to-edge on Android 15+, and Android 16 removed
|
|
84
|
+
// the opt-out. With edgeToEdge off in the config, the shell pads its root view by the
|
|
85
|
+
// system-bar insets instead, so content still sits between the bars.
|
|
86
|
+
private val systemDrawsEdgeToEdge: Boolean
|
|
87
|
+
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
|
|
88
|
+
|
|
80
89
|
private fun configureEdgeToEdge() {
|
|
81
90
|
edgeToEdgeEnabled = EdgeToEdgeUtils.isEnabled(properties)
|
|
82
91
|
|
|
@@ -119,6 +128,16 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
119
128
|
// Track inset changes (first layout, rotation, cutout, bar visibility); insets stay unconsumed
|
|
120
129
|
var lastCachedInsets = cachedInsets
|
|
121
130
|
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
|
|
131
|
+
// edgeToEdge off on Android 15+: pad by the bars, and hand the children insets
|
|
132
|
+
// without them so the WebView doesn't inset its content a second time.
|
|
133
|
+
var childInsets = insets
|
|
134
|
+
if (!edgeToEdgeEnabled && systemDrawsEdgeToEdge) {
|
|
135
|
+
val barTypes = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
|
|
136
|
+
val bars = insets.getInsets(barTypes)
|
|
137
|
+
view.setPadding(bars.left, bars.top, bars.right, bars.bottom)
|
|
138
|
+
childInsets = WindowInsetsCompat.Builder(insets).setInsets(barTypes, Insets.NONE).build()
|
|
139
|
+
}
|
|
140
|
+
|
|
122
141
|
val updatedInsets = SafeAreaUtils.fromWindowInsets(
|
|
123
142
|
insets,
|
|
124
143
|
edgeToEdgeEnabled,
|
|
@@ -141,7 +160,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
141
160
|
lastCachedInsets = updatedInsets
|
|
142
161
|
}
|
|
143
162
|
|
|
144
|
-
|
|
163
|
+
childInsets
|
|
145
164
|
}
|
|
146
165
|
ViewCompat.requestApplyInsets(rootView)
|
|
147
166
|
}
|
|
@@ -327,7 +346,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
327
346
|
}
|
|
328
347
|
|
|
329
348
|
// Initialize keyboard utility (pass webView so keyboard events reach WebPerfCollector)
|
|
330
|
-
keyboardUtil = KeyboardUtil(
|
|
349
|
+
keyboardUtil = KeyboardUtil(
|
|
350
|
+
this,
|
|
351
|
+
binding.webviewContainer,
|
|
352
|
+
binding.webview,
|
|
353
|
+
edgeToEdgeEnabled || systemDrawsEdgeToEdge
|
|
354
|
+
)
|
|
331
355
|
keyboardUtil.initialize()
|
|
332
356
|
|
|
333
357
|
// Enable hardware acceleration for the window
|
|
@@ -381,7 +405,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
381
405
|
setupSafeAreaHandling()
|
|
382
406
|
|
|
383
407
|
// Setup back press handler (modern API)
|
|
384
|
-
|
|
408
|
+
// Android 16 turns predictive back on by default; OnBackPressedDispatcher is the
|
|
409
|
+
// supported path for it. The callback steps aside to let the system leave the app
|
|
410
|
+
// and is re-armed in onResume, since a task moved to the back keeps this activity.
|
|
411
|
+
backCallback = object : OnBackPressedCallback(true) {
|
|
385
412
|
override fun handleOnBackPressed() {
|
|
386
413
|
if (customWebView.canGoBack()) {
|
|
387
414
|
if (BuildConfig.DEBUG) {
|
|
@@ -397,7 +424,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
397
424
|
onBackPressedDispatcher.onBackPressed()
|
|
398
425
|
}
|
|
399
426
|
}
|
|
400
|
-
})
|
|
427
|
+
}.also { onBackPressedDispatcher.addCallback(this, it) }
|
|
401
428
|
|
|
402
429
|
// Load URL based on environment and deep link data
|
|
403
430
|
try {
|
|
@@ -471,6 +498,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
471
498
|
|
|
472
499
|
override fun onResume() {
|
|
473
500
|
super.onResume()
|
|
501
|
+
backCallback?.isEnabled = true
|
|
474
502
|
customWebView.onResume()
|
|
475
503
|
}
|
|
476
504
|
|
|
@@ -18,6 +18,7 @@ class KeyboardUtil(
|
|
|
18
18
|
private val activity: Activity,
|
|
19
19
|
private val webViewContainer: View,
|
|
20
20
|
private val webView: WebView? = null,
|
|
21
|
+
// True whenever the window draws behind the system bars: edgeToEdge on, or Android 15+.
|
|
21
22
|
private val edgeToEdgeEnabled: Boolean = false,
|
|
22
23
|
) {
|
|
23
24
|
private var originalHeight: Int = 0
|
|
@@ -81,9 +82,12 @@ class KeyboardUtil(
|
|
|
81
82
|
val keyboardHeight = if (edgeToEdgeEnabled) {
|
|
82
83
|
// Edge-to-edge: rootView spans behind the system bars, so measure how far the
|
|
83
84
|
// visible frame's bottom sits above rootView's bottom (screen coords; works in split-screen)
|
|
85
|
+
// The parent's bottom padding is the navigation bar when MainActivity pads for
|
|
86
|
+
// system bars (edgeToEdge off on Android 15+); the keyboard covers that strip.
|
|
84
87
|
val location = IntArray(2)
|
|
85
88
|
rootView.getLocationOnScreen(location)
|
|
86
|
-
|
|
89
|
+
val parentBottomPadding = (webViewContainer.parent as? View)?.paddingBottom ?: 0
|
|
90
|
+
maxOf(0, (location[1] + rootView.height) - rect.bottom - parentBottomPadding)
|
|
87
91
|
} else {
|
|
88
92
|
val visibleHeight = rect.height()
|
|
89
93
|
screenHeight - visibleHeight
|
package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ConfigMappingTest.kt
CHANGED
|
@@ -26,6 +26,8 @@ class ConfigMappingTest {
|
|
|
26
26
|
|
|
27
27
|
while (keys.hasNext()) {
|
|
28
28
|
val key = keys.next()
|
|
29
|
+
// The release override block is merged by the CLI, never shipped as-is.
|
|
30
|
+
if (prefix.isEmpty() && key == "release") continue
|
|
29
31
|
val value = jsonObj.opt(key)
|
|
30
32
|
val fullKey = if (prefix.isEmpty()) key else "$prefix.$key"
|
|
31
33
|
|
|
@@ -232,6 +234,29 @@ class ConfigMappingTest {
|
|
|
232
234
|
assertEquals("3005", properties.getProperty("port"))
|
|
233
235
|
}
|
|
234
236
|
|
|
237
|
+
@Test
|
|
238
|
+
fun `test top-level release override block is not flattened`() {
|
|
239
|
+
val json = JSONObject().apply {
|
|
240
|
+
put("LOCAL_IP", "192.168.1.100")
|
|
241
|
+
put("release", JSONObject().apply {
|
|
242
|
+
put("LOCAL_IP", "www.example.com")
|
|
243
|
+
put("allowPrivateHost", false)
|
|
244
|
+
})
|
|
245
|
+
put("android", JSONObject().apply {
|
|
246
|
+
put("release", "kept")
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
val properties = Properties()
|
|
251
|
+
extractProperties(json, properties)
|
|
252
|
+
|
|
253
|
+
assertEquals("192.168.1.100", properties.getProperty("LOCAL_IP"))
|
|
254
|
+
assertNull(properties.getProperty("release.LOCAL_IP"))
|
|
255
|
+
assertNull(properties.getProperty("release.allowPrivateHost"))
|
|
256
|
+
// Only the top-level key is reserved
|
|
257
|
+
assertEquals("kept", properties.getProperty("android.release"))
|
|
258
|
+
}
|
|
259
|
+
|
|
235
260
|
// ============================================================
|
|
236
261
|
// Test Category 2: Build Type Handling (4 tests)
|
|
237
262
|
// ============================================================
|
|
@@ -4,154 +4,63 @@ var _fs = _interopRequireDefault(require("fs"));
|
|
|
4
4
|
var _path = _interopRequireDefault(require("path"));
|
|
5
5
|
var _child_process = require("child_process");
|
|
6
6
|
var _utils = require("./utils.js");
|
|
7
|
+
var _androidToolchain = require("./androidToolchain.js");
|
|
8
|
+
var _localState = require("./localState.js");
|
|
7
9
|
var _terminalProgress = _interopRequireDefault(require("./terminalProgress.js"));
|
|
8
|
-
var
|
|
10
|
+
var _diagnostic = require("../cli/diagnostic.js");
|
|
9
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
// setupEmulator:android installs what an Android build needs (JDK, SDK
|
|
13
|
+
// packages, an emulator) and starts the emulator. It changes nothing in the
|
|
14
|
+
// app: config/config.json is read for sdkPath and emulatorName when they are
|
|
15
|
+
// set, never written. buildApp:android does the same checks itself, so this
|
|
16
|
+
// command is optional.
|
|
17
|
+
|
|
10
18
|
const catalystCorePath = _path.default.dirname(require.resolve("catalyst-core/package.json"));
|
|
11
19
|
const pwd = _path.default.join(catalystCorePath, "dist/native");
|
|
12
|
-
const configPath =
|
|
20
|
+
const configPath = _path.default.join(process.cwd(), "config", "config.json");
|
|
21
|
+
const flags = (0, _utils.parseNativeArgs)(process.argv.slice(2));
|
|
13
22
|
|
|
14
23
|
// Sentence case, verb first: these read as a sequence of actions, not as
|
|
15
24
|
// headings.
|
|
16
25
|
const steps = {
|
|
17
|
-
java: "Check Java
|
|
18
|
-
|
|
26
|
+
java: "Check Java (JDK 17 to 23)",
|
|
27
|
+
sdk: "Set up the Android SDK",
|
|
19
28
|
androidTools: "Validate Android tools",
|
|
20
|
-
emulator: "
|
|
21
|
-
properties: "Update local.properties"
|
|
22
|
-
saveConfig: "Save configuration",
|
|
23
|
-
setupServer: "Set up server"
|
|
29
|
+
emulator: "Start the Android emulator",
|
|
30
|
+
properties: "Update local.properties"
|
|
24
31
|
};
|
|
25
32
|
const progress = new _terminalProgress.default(steps, "catalyst setupEmulator", {
|
|
26
33
|
subject: "android"
|
|
27
34
|
});
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
(0, _utils.runCommand)("java -version");
|
|
31
|
-
(0, _utils.runCommand)("javac -version");
|
|
32
|
-
return true;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
progress.log("Java installation not found or not properly configured", "error");
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async function installJava() {
|
|
39
|
-
progress.log("Installing Java (Zulu JDK 17)...");
|
|
40
|
-
try {
|
|
41
|
-
try {
|
|
42
|
-
(0, _utils.runCommand)("brew --version");
|
|
43
|
-
} catch (error) {
|
|
44
|
-
throw new Error("Homebrew is required but not installed. Please install Homebrew first.");
|
|
45
|
-
}
|
|
46
|
-
const javaHome = "/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home";
|
|
47
|
-
|
|
48
|
-
// Try installing Java
|
|
49
|
-
try {
|
|
50
|
-
await (0, _utils.runCommand)("brew install --cask zulu@17");
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (error.message.includes("already installed")) {
|
|
53
|
-
progress.log("Attempting to reinstall Java...", "info");
|
|
54
|
-
try {
|
|
55
|
-
await (0, _utils.runCommand)("brew reinstall --cask zulu@17");
|
|
56
|
-
} catch (reinstallError) {
|
|
57
|
-
throw new Error(`Failed to reinstall Java: ${reinstallError.message}. Please run 'brew reinstall --cask zulu@17' manually.`);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Verify Java installation
|
|
65
|
-
if (!_fs.default.existsSync(javaHome)) {
|
|
66
|
-
throw new Error(`Java installation not found at ${javaHome}. Please verify the installation manually.`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Set up environment variables
|
|
70
|
-
const homeDir = process.env.HOME;
|
|
71
|
-
const shellProfile = `${homeDir}/.zshrc`;
|
|
72
|
-
const exportCommand = `\n# Java Configuration\nexport JAVA_HOME=${javaHome}\nexport PATH=$JAVA_HOME/bin:$PATH\n`;
|
|
73
|
-
try {
|
|
74
|
-
_fs.default.appendFileSync(shellProfile, exportCommand);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
throw new Error(`Failed to update ${shellProfile}: ${error.message}. Please add the following lines manually:\n${exportCommand}`);
|
|
77
|
-
}
|
|
35
|
+
const log = (message, type = "info") => progress.log(message, type);
|
|
78
36
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Verify Java configuration
|
|
84
|
-
try {
|
|
85
|
-
const javaVersion = (0, _utils.runCommand)("java -version");
|
|
86
|
-
progress.log("Installed Java 17 (Zulu)", "info");
|
|
87
|
-
progress.log(`Java Version: ${javaVersion}`, "info");
|
|
88
|
-
progress.log(`IMPORTANT: Please run the following commands to complete setup:`, "warning");
|
|
89
|
-
progress.log(`1. source ${shellProfile}`, "note");
|
|
90
|
-
progress.log(`2. echo $JAVA_HOME`, "note");
|
|
91
|
-
return true;
|
|
92
|
-
} catch (error) {
|
|
93
|
-
throw new Error(`Java installation verified but environment not configured. Please run:\n1. source ${shellProfile}\n2. echo $JAVA_HOME`);
|
|
94
|
-
}
|
|
95
|
-
} catch (error) {
|
|
96
|
-
progress.log(error.message, "error");
|
|
97
|
-
progress.log("Manual Setup Instructions:", "note");
|
|
98
|
-
progress.log("1. Run: brew reinstall --cask zulu@17", "note");
|
|
99
|
-
progress.log("2. Add to ~/.zshrc:", "note");
|
|
100
|
-
progress.log(" export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home", "note");
|
|
101
|
-
progress.log(" export PATH=$JAVA_HOME/bin:$PATH", "note");
|
|
102
|
-
progress.log("3. Run: source ~/.zshrc", "note");
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
37
|
+
/**
|
|
38
|
+
* A usable JDK for this process and every tool it starts (Gradle, sdkmanager,
|
|
39
|
+
* avdmanager). JAVA_HOME is set here only; no shell profile is edited.
|
|
40
|
+
*/
|
|
106
41
|
async function validateJavaEnvironment() {
|
|
107
42
|
progress.start("java");
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
throw new Error("Failed to set up Java environment");
|
|
115
|
-
}
|
|
116
|
-
}
|
|
43
|
+
const java = await (0, _androidToolchain.ensureJava)({
|
|
44
|
+
flags,
|
|
45
|
+
log
|
|
46
|
+
});
|
|
47
|
+
(0, _androidToolchain.applyJavaHome)(java);
|
|
48
|
+
progress.log(`Java ${java.version} from ${java.source} (${java.home})`, "info");
|
|
117
49
|
progress.complete("java");
|
|
118
50
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const {
|
|
123
|
-
WEBVIEW_CONFIG
|
|
124
|
-
} = config;
|
|
125
|
-
if (!WEBVIEW_CONFIG || Object.keys(WEBVIEW_CONFIG).length === 0) {
|
|
126
|
-
progress.log("WebView Config missing in " + configPath, "error");
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
if (!WEBVIEW_CONFIG.android) {
|
|
130
|
-
WEBVIEW_CONFIG.android = {};
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
WEBVIEW_CONFIG
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
async function saveConfig(newConfig) {
|
|
51
|
+
|
|
52
|
+
/** WEBVIEW_CONFIG.android from config.json, or {} when there is none. Read only. */
|
|
53
|
+
function readAndroidConfig() {
|
|
137
54
|
try {
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
...existingConfig.WEBVIEW_CONFIG,
|
|
144
|
-
android: newConfig.WEBVIEW_CONFIG.android
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
_fs.default.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2));
|
|
148
|
-
} catch (error) {
|
|
149
|
-
progress.log("Failed to save configuration: " + error.message, "error");
|
|
150
|
-
process.exit(1);
|
|
55
|
+
const config = JSON.parse(_fs.default.readFileSync(configPath, "utf8"));
|
|
56
|
+
const android = config?.WEBVIEW_CONFIG?.android;
|
|
57
|
+
return android && typeof android === "object" ? android : {};
|
|
58
|
+
} catch {
|
|
59
|
+
return {};
|
|
151
60
|
}
|
|
152
61
|
}
|
|
153
62
|
function validateAndroidTools(androidConfig) {
|
|
154
|
-
const ANDROID_SDK =
|
|
63
|
+
const ANDROID_SDK = androidConfig.sdkPath;
|
|
155
64
|
const EMULATOR_PATH = `${ANDROID_SDK}/emulator/emulator`;
|
|
156
65
|
const ADB_PATH = `${ANDROID_SDK}/platform-tools/adb`;
|
|
157
66
|
if (!ANDROID_SDK) {
|
|
@@ -166,7 +75,7 @@ function validateAndroidTools(androidConfig) {
|
|
|
166
75
|
throw new Error(`ADB not found at: ${ADB_PATH}`);
|
|
167
76
|
}
|
|
168
77
|
try {
|
|
169
|
-
(0, _utils.
|
|
78
|
+
(0, _utils.runFile)(ADB_PATH, ["version"]);
|
|
170
79
|
} catch (error) {
|
|
171
80
|
throw new Error(`ADB is not working properly: ${error.message}`);
|
|
172
81
|
}
|
|
@@ -174,12 +83,12 @@ function validateAndroidTools(androidConfig) {
|
|
|
174
83
|
throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);
|
|
175
84
|
}
|
|
176
85
|
try {
|
|
177
|
-
(0, _utils.
|
|
86
|
+
(0, _utils.runFile)(EMULATOR_PATH, ["-version"]);
|
|
178
87
|
} catch (error) {
|
|
179
88
|
throw new Error(`Emulator is not working properly: ${error.message}`);
|
|
180
89
|
}
|
|
181
90
|
try {
|
|
182
|
-
const avdOutput = (0, _utils.
|
|
91
|
+
const avdOutput = (0, _utils.runFile)(EMULATOR_PATH, ["-list-avds"]);
|
|
183
92
|
if (!avdOutput.includes(androidConfig.emulatorName)) {
|
|
184
93
|
throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);
|
|
185
94
|
}
|
|
@@ -194,7 +103,7 @@ function validateAndroidTools(androidConfig) {
|
|
|
194
103
|
}
|
|
195
104
|
async function checkEmulator(ADB_PATH) {
|
|
196
105
|
try {
|
|
197
|
-
const devices = (0, _utils.
|
|
106
|
+
const devices = (0, _utils.runFile)(ADB_PATH, ["devices"], {
|
|
198
107
|
timeout: 15000
|
|
199
108
|
});
|
|
200
109
|
return devices.includes("emulator");
|
|
@@ -215,75 +124,72 @@ async function startEmulator(EMULATOR_PATH, androidConfig) {
|
|
|
215
124
|
});
|
|
216
125
|
child.unref();
|
|
217
126
|
}
|
|
218
|
-
|
|
127
|
+
function updateLocalProperties(sdkPath) {
|
|
219
128
|
progress.start("properties");
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
progress.complete("properties");
|
|
223
|
-
} catch (error) {
|
|
224
|
-
progress.fail("properties", `Failed to update local.properties: ${error.message}`);
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
129
|
+
(0, _utils.ensureLocalProperties)(`${pwd}/androidProject`, sdkPath);
|
|
130
|
+
progress.complete("properties");
|
|
227
131
|
}
|
|
228
132
|
async function setupAndroidEnvironment() {
|
|
229
133
|
try {
|
|
230
134
|
await validateJavaEnvironment();
|
|
231
|
-
progress.start("
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
135
|
+
progress.start("sdk");
|
|
136
|
+
const fileAndroid = readAndroidConfig();
|
|
137
|
+
const sdkPath = (0, _androidToolchain.resolveSdkPath)(fileAndroid);
|
|
138
|
+
const last = (0, _localState.getLastTarget)("android");
|
|
139
|
+
const sdk = await (0, _androidToolchain.ensureAndroidSdk)({
|
|
140
|
+
sdkPath,
|
|
141
|
+
emulatorName: fileAndroid.emulatorName,
|
|
142
|
+
preferredAvd: last?.target === "emulator" ? last.avd : undefined,
|
|
143
|
+
flags,
|
|
144
|
+
log
|
|
145
|
+
});
|
|
146
|
+
if (sdk.installed.length) progress.log(`Installed ${sdk.installed.join(", ")}`, "success");
|
|
147
|
+
if (sdk.createdAvd) progress.log(`Created the "${sdk.emulatorName}" emulator`, "success");
|
|
148
|
+
progress.complete("sdk");
|
|
149
|
+
const androidConfig = {
|
|
150
|
+
...fileAndroid,
|
|
151
|
+
sdkPath,
|
|
152
|
+
emulatorName: sdk.emulatorName
|
|
153
|
+
};
|
|
235
154
|
progress.start("androidTools");
|
|
236
155
|
const {
|
|
237
156
|
EMULATOR_PATH,
|
|
238
157
|
ADB_PATH
|
|
239
|
-
} = validateAndroidTools(
|
|
158
|
+
} = validateAndroidTools(androidConfig);
|
|
240
159
|
progress.complete("androidTools");
|
|
241
160
|
progress.start("emulator");
|
|
242
161
|
const emulatorRunning = await checkEmulator(ADB_PATH);
|
|
243
162
|
if (!emulatorRunning) {
|
|
244
163
|
progress.log("No emulator running, attempting to start one...");
|
|
245
|
-
await startEmulator(EMULATOR_PATH,
|
|
164
|
+
await startEmulator(EMULATOR_PATH, androidConfig);
|
|
246
165
|
} else {
|
|
247
166
|
progress.log("Emulator already running", "info");
|
|
248
167
|
}
|
|
249
168
|
progress.complete("emulator");
|
|
250
|
-
|
|
251
|
-
await updateLocalProperties((0, _utils.expandHome)(config.android.sdkPath));
|
|
252
|
-
}
|
|
253
|
-
progress.start("saveConfig");
|
|
254
|
-
await saveConfig({
|
|
255
|
-
WEBVIEW_CONFIG: config
|
|
256
|
-
});
|
|
257
|
-
progress.complete("saveConfig");
|
|
258
|
-
progress.start("setupServer");
|
|
259
|
-
await (0, _setupServer.setupServer)(configPath, message => progress.log(message, "info"));
|
|
260
|
-
progress.complete("setupServer");
|
|
169
|
+
updateLocalProperties(sdkPath);
|
|
261
170
|
|
|
262
|
-
// What
|
|
171
|
+
// What this machine uses, not what the keys mean: the explanation
|
|
263
172
|
// belongs in the docs, not in every successful run's transcript.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const androidCfg = config.android || {};
|
|
267
|
-
progress.printTreeContent("Config config/config.json", [{
|
|
268
|
-
text: `port ${config.port ?? ""}`,
|
|
269
|
-
color: "gray"
|
|
270
|
-
}, {
|
|
271
|
-
text: `buildType ${androidCfg.buildType ?? ""}`,
|
|
272
|
-
color: "gray"
|
|
273
|
-
}, {
|
|
274
|
-
text: `sdkPath ${androidCfg.sdkPath ?? ""}`,
|
|
173
|
+
progress.printTreeContent("Android", [{
|
|
174
|
+
text: `sdk ${sdkPath}`,
|
|
275
175
|
color: "gray"
|
|
276
176
|
}, {
|
|
277
|
-
text: `
|
|
177
|
+
text: `emulator ${sdk.emulatorName}`,
|
|
278
178
|
color: "gray"
|
|
279
179
|
}]);
|
|
280
180
|
progress.summary("Ready", "Run catalyst buildApp:android to install the app");
|
|
281
181
|
process.exit(0);
|
|
282
182
|
} catch (error) {
|
|
183
|
+
// A coded error (ANDROID-007, NATIVE-005, ...) gets the full diagnostic:
|
|
184
|
+
// code, hint and doc link. Anything else keeps the one-line failure.
|
|
185
|
+
const coded = /^[A-Z]+-\d{3}$/.test(String(error?.code || ""));
|
|
283
186
|
if (progress.currentStep) {
|
|
284
|
-
progress.fail(progress.currentStep.id, error.message);
|
|
187
|
+
progress.fail(progress.currentStep.id, coded ? undefined : error.message);
|
|
285
188
|
}
|
|
286
|
-
|
|
189
|
+
if (coded) process.stderr.write((0, _diagnostic.buildFailure)({
|
|
190
|
+
error,
|
|
191
|
+
stage: "Android setup"
|
|
192
|
+
}));else progress.hint(error.hint);
|
|
287
193
|
process.exit(1);
|
|
288
194
|
}
|
|
289
195
|
}
|