catalyst-core 0.2.0-beta.2 → 0.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/babel.native.config.cjs +8 -0
  2. package/bin/catalyst.js +49 -105
  3. package/changelog.md +8 -0
  4. package/dist/caching.js +1 -1
  5. package/dist/index.jsx +8 -0
  6. package/dist/logger.js +74 -3
  7. package/dist/native/androidProject/app/build.gradle.kts +1 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
  16. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
  17. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
  19. package/dist/native/androidSetup.js +300 -8
  20. package/dist/native/bridge/WebBridge.js +481 -59
  21. package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
  22. package/dist/native/bridge/errors.js +250 -26
  23. package/dist/native/bridge/hooks/index.js +125 -1
  24. package/dist/native/bridge/hooks/useCamera.js +347 -9
  25. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  26. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  27. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  28. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  29. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  30. package/dist/native/bridge/hooks/useIntent.js +95 -1
  31. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  32. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  33. package/dist/native/bridge/hooks/useNotification.js +318 -2
  34. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  35. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  36. package/dist/native/bridge/hooks/utils.js +27 -1
  37. package/dist/native/bridge/hooks.js +16 -1
  38. package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
  39. package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
  40. package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
  41. package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
  42. package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
  43. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
  44. package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
  45. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
  46. package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
  47. package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
  48. package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
  49. package/dist/native/bridge/perf/core/constants.js +126 -0
  50. package/dist/native/bridge/perf/core/measure.js +189 -0
  51. package/dist/native/bridge/perf/core/session.js +100 -0
  52. package/dist/native/bridge/perf/core/store.js +554 -0
  53. package/dist/native/bridge/perf/core/summary.js +174 -0
  54. package/dist/native/bridge/perf/core/traceExport.js +240 -0
  55. package/dist/native/bridge/perf/core/waterfalls.js +146 -0
  56. package/dist/native/bridge/perf/index.js +402 -0
  57. package/dist/native/bridge/safeArea.js +53 -5
  58. package/dist/native/bridge/standardizedHooks.js +595 -82
  59. package/dist/native/bridge/useBaseHook.js +274 -38
  60. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  61. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  62. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  63. package/dist/native/buildAndroid/assets.js +510 -13
  64. package/dist/native/buildAndroid/build.js +296 -19
  65. package/dist/native/buildAndroid/config.js +87 -1
  66. package/dist/native/buildAndroid/index.js +374 -1
  67. package/dist/native/buildAppAndroid.js +34 -2
  68. package/dist/native/buildAppIos.js +62 -2
  69. package/dist/native/buildIos/assets.js +609 -8
  70. package/dist/native/buildIos/build.js +639 -20
  71. package/dist/native/buildIos/config.js +294 -12
  72. package/dist/native/buildIos/index.js +343 -6
  73. package/dist/native/buildIos/plugins.js +343 -16
  74. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  75. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  76. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  77. package/dist/native/internalPluginUtils.js +232 -7
  78. package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
  79. package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
  80. package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
  81. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
  82. package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
  83. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
  84. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
  85. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
  86. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
  87. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
  88. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
  89. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
  90. package/dist/native/package.json +3 -0
  91. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  92. package/dist/native/pluginComposerAndroid.js +407 -23
  93. package/dist/native/pluginComposerIos.js +435 -11
  94. package/dist/native/renameAndroidProject.js +1094 -88
  95. package/dist/native/setupEmulatorIos.js +289 -22
  96. package/dist/native/setupServer.js +160 -19
  97. package/dist/native/terminalProgress.js +199 -11
  98. package/dist/native/utils.js +193 -15
  99. package/dist/offline/catalyst-sw.js +145 -1
  100. package/dist/offline/registerServiceWorker.js +33 -1
  101. package/dist/otel.js +375 -46
  102. package/dist/router/ClientRouter.js +14 -2
  103. package/dist/router/ServerRouter.js +9 -1
  104. package/dist/router.js +1 -1
  105. package/dist/scripts/build.js +112 -3
  106. package/dist/scripts/checkVersion.js +3 -1
  107. package/dist/scripts/cleanCache.js +30 -1
  108. package/dist/scripts/generateOfflineManifest.js +116 -1
  109. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  110. package/dist/scripts/plugins.js +309 -1
  111. package/dist/scripts/preServerInit.js +13 -0
  112. package/dist/scripts/scriptUtils.js +54 -3
  113. package/dist/scripts/serve.js +51 -4
  114. package/dist/scripts/start.js +47 -10
  115. package/dist/scripts/validator.js +149 -1
  116. package/dist/sentry.cjs +127 -0
  117. package/dist/sentry.js +125 -5
  118. package/dist/server/expressServer.js +250 -5
  119. package/dist/server/manifestCache.js +36 -0
  120. package/dist/server/renderer/ChunkExtractor.js +144 -0
  121. package/dist/server/renderer/document/Body.jsx +59 -0
  122. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  123. package/dist/server/renderer/document/Head.jsx +60 -0
  124. package/dist/server/renderer/document/index.jsx +2 -0
  125. package/dist/server/renderer/extract.js +149 -31
  126. package/dist/server/renderer/handler.jsx +433 -0
  127. package/dist/server/renderer/index.js +8 -1
  128. package/dist/server/renderer/render.js +47 -0
  129. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  130. package/dist/server/utils/cjsRequire.js +10 -0
  131. package/dist/server/utils/userAgentUtil.js +87 -9
  132. package/dist/server/utils/validator.js +164 -1
  133. package/dist/vite/FastRefresh.jsx +20 -0
  134. package/dist/vite/inject-cache-key-plugin.js +276 -0
  135. package/dist/vite/loadCustomViteConfig.js +22 -0
  136. package/dist/vite/manifest-categorization-plugin.js +455 -0
  137. package/dist/vite/node-loader.mjs +458 -0
  138. package/dist/vite/scssParams.js +21 -0
  139. package/dist/vite/vite.config.client.js +130 -0
  140. package/dist/vite/vite.config.js +422 -0
  141. package/dist/vite/vite.config.server.js +84 -0
  142. package/dist/web-router/components/MetaTag.jsx +34 -0
  143. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  144. package/dist/web-router/components/Split.jsx +200 -0
  145. package/dist/web-router/components/SplitInview.jsx +106 -0
  146. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  147. package/dist/web-router/context.jsx +12 -0
  148. package/dist/web-router/hooks.jsx +72 -0
  149. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  150. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  151. package/dist/web-router/types/context.d.ts +14 -0
  152. package/dist/web-router/types/hooks.d.ts +1 -0
  153. package/dist/web-router/types/index.d.ts +3 -0
  154. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  155. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  156. package/mcp_v2/conversion-tasks.json +35 -17
  157. package/mcp_v2/knowledge-base.json +124 -13
  158. package/mcp_v2/lib/helpers.js +10 -0
  159. package/mcp_v2/mcp.js +4 -4
  160. package/mcp_v2/package.json +1 -0
  161. package/mcp_v2/tools/build.js +116 -20
  162. package/mcp_v2/tools/conversion.js +106 -5
  163. package/mcp_v2/tools/tasks.js +18 -14
  164. package/package.json +35 -68
  165. package/babel.config.js +0 -32
  166. package/dist/index.js +0 -1
  167. package/dist/scripts/devBuild.js +0 -3
  168. package/dist/scripts/devServe.js +0 -3
  169. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  170. package/dist/scripts/registerAliases.js +0 -6
  171. package/dist/server/renderer/document/Body.js +0 -11
  172. package/dist/server/renderer/document/Head.js +0 -9
  173. package/dist/server/renderer/document/index.js +0 -1
  174. package/dist/server/renderer/handler.js +0 -47
  175. package/dist/server/startServer.js +0 -57
  176. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  177. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  178. package/dist/webpack/babel.config.client.js +0 -1
  179. package/dist/webpack/babel.config.ssr.js +0 -1
  180. package/dist/webpack/base.babel.js +0 -7
  181. package/dist/webpack/development.client.babel.js +0 -10
  182. package/dist/webpack/production.client.babel.js +0 -4
  183. package/dist/webpack/production.ssr.babel.js +0 -8
  184. package/dist/webpack/scssParams.js +0 -9
@@ -1,89 +1,1095 @@
1
- /* eslint-disable no-extra-semi */"use strict";var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _utils=require("./utils.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}// Simple progress logging without TerminalProgress dependency
2
- class SimpleProgress{constructor(title){this.title=title;this.currentStep=null;console.log(`\n=== ${title} ===\n`);}start(stepId){this.currentStep={id:stepId};console.log(`⏳ Starting: ${stepId}`);}complete(stepId){console.log(`✅ Completed: ${stepId}`);this.currentStep=null;}fail(stepId,message){console.log(`❌ Failed: ${stepId} - ${message}`);}log(message,type="info"){const prefix=type==="error"?"❌":type==="warning"?"⚠️ ":type==="success"?"✅":"ℹ️ ";console.log(` ${prefix} ${message}`);}printTreeContent(title,items){console.log(`\n📋 ${title}:`);items.forEach(item=>{if(typeof item==="string"){console.log(item);}else{const indent=" ".repeat(item.indent||0);const prefix=item.prefix||"";console.log(`${indent}${prefix}${item.text}`);}});console.log("");}}const progress=new SimpleProgress("Android AAB Builder");async function createKeystore(projectPaths,androidConfig){const{newProjectPath}=projectPaths;const{keystoreConfig}=androidConfig;if(!keystoreConfig){progress.log("Keystore configuration not found, skipping keystore creation","info");return null;}progress.log("Checking keystore configuration...","info");// Validate keystore passwords are not placeholder values
3
- if(keystoreConfig.storePassword==="your_store_password"||keystoreConfig.keyPassword==="your_key_password"||keystoreConfig.storePassword==="your_password"||keystoreConfig.keyPassword==="your_password"){throw new Error('Please update keystore passwords in config file. Do not use placeholder values like "your_store_password" or "your_key_password".');}// Define keystore path
4
- const keystorePath=_path.default.join(newProjectPath,"app","keystore.jks");// Check if keystore already exists
5
- if(_fs.default.existsSync(keystorePath)){progress.log("Keystore already exists, verifying accessibility...","info");// Test keystore accessibility
6
- try{const testCommand=`keytool -list -keystore "${keystorePath}" -storepass "${keystoreConfig.storePassword}" -alias "${keystoreConfig.keyAlias}"`;(0,_utils.runCommand)(testCommand);progress.log("Existing keystore verified successfully","success");return keystorePath;}catch(testError){progress.log("Existing keystore verification failed, creating new one...","warning");// Remove the faulty keystore
7
- _fs.default.unlinkSync(keystorePath);}}progress.log("Creating new keystore...","info");try{// Validate required keystore configuration
8
- const requiredFields=["keyAlias","storePassword","keyPassword","organizationInfo"];for(const field of requiredFields){if(!keystoreConfig[field]){throw new Error(`Missing required keystore config field: ${field}`);}}const{organizationInfo}=keystoreConfig;const requiredOrgFields=["companyName","city","state","countryCode"];for(const field of requiredOrgFields){if(!organizationInfo[field]){throw new Error(`Missing required organization info field: ${field}`);}}// Create keystore directory if it doesn't exist
9
- const keystoreDir=_path.default.dirname(keystorePath);if(!_fs.default.existsSync(keystoreDir)){_fs.default.mkdirSync(keystoreDir,{recursive:true});}// Build the distinguished name (DN) for the certificate
10
- const dn=[`CN=${organizationInfo.companyName}`,`OU=${organizationInfo.department||organizationInfo.companyName}`,`O=${organizationInfo.organization||organizationInfo.companyName}`,`L=${organizationInfo.city}`,`ST=${organizationInfo.state}`,`C=${organizationInfo.countryCode}`].join(", ");// For PKCS12 keystores (default), store and key passwords must be the same
11
- // Use store password for both to avoid PKCS12 compatibility issues
12
- const effectiveKeyPassword=keystoreConfig.storePassword;if(keystoreConfig.keyPassword!==keystoreConfig.storePassword){progress.log("Note: Using store password for key password (PKCS12 requirement)","info");}// Generate keystore using keytool with PKCS12 format explicitly
13
- const validityDays=(keystoreConfig.validityYears||25)*365;const keystoreCommand=["keytool","-genkeypair","-v",`-storetype PKCS12`,`-keystore "${keystorePath}"`,`-alias "${keystoreConfig.keyAlias}"`,`-keyalg RSA`,`-keysize 2048`,`-validity ${validityDays}`,`-storepass "${keystoreConfig.storePassword}"`,`-keypass "${effectiveKeyPassword}"`,`-dname "${dn}"`].join(" ");progress.log("Executing keytool command...","info");(0,_utils.runCommand)(keystoreCommand);// Verify keystore was created
14
- if(!_fs.default.existsSync(keystorePath)){throw new Error("Keystore file was not created successfully");}// Test the newly created keystore
15
- try{const testCommand=`keytool -list -keystore "${keystorePath}" -storepass "${keystoreConfig.storePassword}" -alias "${keystoreConfig.keyAlias}"`;(0,_utils.runCommand)(testCommand);progress.log("New keystore verified successfully","success");}catch(testError){throw new Error(`Created keystore failed verification: ${testError.message}`);}progress.log(`Keystore created successfully at: ${keystorePath}`,"success");progress.log(`Key alias: ${keystoreConfig.keyAlias}`,"info");progress.log(`Validity: ${keystoreConfig.validityYears||25} years`,"info");progress.log(`Store type: PKCS12`,"info");return keystorePath;}catch(error){throw new Error(`Failed to create keystore: ${error.message}`);}}async function createSignedAAB(projectPaths,androidConfig,keystorePath){const{newProjectPath}=projectPaths;const{keystoreConfig}=androidConfig;if(!keystoreConfig||!keystorePath){throw new Error("Keystore configuration and keystore path are required for signed AAB creation");}progress.log("Building signed Android App Bundle (AAB)...","info");try{// Verify keystore exists
16
- if(!_fs.default.existsSync(keystorePath)){throw new Error(`Keystore not found at: ${keystorePath}`);}// Get absolute path for the project directory
17
- const absoluteProjectPath=_path.default.resolve(newProjectPath);// Change to project directory
18
- const originalCwd=process.cwd();process.chdir(absoluteProjectPath);progress.log(`Changed to project directory: ${absoluteProjectPath}`,"info");// Verify gradlew exists using absolute path
19
- const gradlewPath=_path.default.join(absoluteProjectPath,"gradlew");progress.log(`Looking for gradlew at: ${gradlewPath}`,"info");if(!_fs.default.existsSync(gradlewPath)){// List files in the project directory for debugging
20
- try{const files=_fs.default.readdirSync(absoluteProjectPath);progress.log(`Files in project directory: ${files.join(", ")}`,"info");// Check if there's a gradlew file with different permissions or name
21
- const gradlewFiles=files.filter(file=>file.toLowerCase().includes("gradle"));if(gradlewFiles.length>0){progress.log(`Found gradle-related files: ${gradlewFiles.join(", ")}`,"info");}}catch(listError){progress.log(`Could not list directory contents: ${listError.message}`,"warning");}throw new Error(`gradlew not found at: ${gradlewPath}. Ensure this is a valid Android project.`);}// Make gradlew executable
22
- try{;(0,_utils.runCommand)(`chmod +x ./gradlew`);}catch(chmodError){progress.log(`Warning: Could not make gradlew executable: ${chmodError.message}`,"warning");}try{// Clean previous builds
23
- progress.log("Cleaning previous builds...","info");try{;(0,_utils.runCommand)("./gradlew clean");progress.log("Clean completed successfully","success");}catch(cleanError){progress.log(`Warning: Clean failed: ${cleanError.message}`,"warning");// Continue anyway as this might not be critical
24
- }// Build signed AAB with PKCS12-compatible parameters
25
- progress.log("Building signed AAB...","info");// For PKCS12 keystores, use store password for both store and key
26
- const effectiveKeyPassword=keystoreConfig.storePassword;const bundleCommand=["./gradlew bundleRelease --quiet --console=rich",`-Pandroid.injected.signing.store.file="${keystorePath}"`,`-Pandroid.injected.signing.store.password="${keystoreConfig.storePassword}"`,`-Pandroid.injected.signing.key.alias="${keystoreConfig.keyAlias}"`,`-Pandroid.injected.signing.key.password="${effectiveKeyPassword}"`].join(" ");progress.log(`Executing: ${bundleCommand}`,"info");(0,_utils.runCommand)(bundleCommand);progress.log("Bundle build completed","success");// Find the generated AAB file
27
- const aabPath=_path.default.join(newProjectPath,"app","build","outputs","bundle","release","app-release.aab");progress.log(`Looking for AAB file at: ${aabPath}`,"info");if(!_fs.default.existsSync(aabPath)){// Try to find AAB files in the build output directory
28
- const bundleDir=_path.default.join(newProjectPath,"app","build","outputs","bundle");progress.log(`AAB not found at expected location. Checking bundle directory: ${bundleDir}`,"warning");if(_fs.default.existsSync(bundleDir)){try{const findAabCommand=`find "${bundleDir}" -name "*.aab" -type f`;const foundAabs=(0,_utils.runCommand)(findAabCommand);if(foundAabs.trim()){progress.log(`Found AAB files: ${foundAabs.trim()}`,"info");}else{progress.log("No AAB files found in bundle directory","warning");}}catch(findError){progress.log(`Error searching for AAB files: ${findError.message}`,"warning");}}throw new Error("Signed AAB file was not generated successfully");}// Get file size
29
- const stats=_fs.default.statSync(aabPath);const fileSizeMB=(stats.size/(1024*1024)).toFixed(2);progress.log(`Signed AAB created successfully!`,"success");progress.log(`AAB location: ${aabPath}`,"info");progress.log(`AAB size: ${fileSizeMB} MB`,"info");// Optionally copy AAB to a more accessible location
30
- const outputDir=_path.default.join(newProjectPath,"release");if(!_fs.default.existsSync(outputDir)){_fs.default.mkdirSync(outputDir,{recursive:true});}const outputAabPath=_path.default.join(outputDir,`${androidConfig.newProjectName}-release.aab`);_fs.default.copyFileSync(aabPath,outputAabPath);progress.log(`AAB copied to: ${outputAabPath}`,"success");return{aabPath,outputAabPath,fileSize:fileSizeMB};}finally{// Restore original working directory
31
- process.chdir(originalCwd);}}catch(error){throw new Error(`Failed to create signed AAB: ${error.message}`);}}async function initializeConfig(configPath){if(!configPath){throw new Error("Config path is required");}if(!_fs.default.existsSync(configPath)){throw new Error(`Config file not found at: ${configPath}`);}progress.log(`Reading config from: ${configPath}`,"info");const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);if(!config.android){throw new Error("Android configuration missing in config file");}const{android}=config;// Validate required fields (only newProjectName is truly required)
32
- if(!android.newProjectName){throw new Error("newProjectName is required in android config");}progress.log(`Project path: ${android.projectPath}`,"info");progress.log(`Old project name: ${android.oldProjectName}`,"info");progress.log(`New project name: ${android.newProjectName}`,"info");if(android.outputPath){progress.log(`Custom output path: ${android.outputPath}`,"info");}else{const parentDir=_path.default.dirname(android.projectPath);const defaultOutputPath=_path.default.join(parentDir,"build-output");progress.log(`Default output path: ${defaultOutputPath}`,"info");}if(android.createSignedAAB){progress.log(`Signed AAB creation: Enabled`,"info");if(android.keystoreConfig){// Check for placeholder passwords
33
- if(android.keystoreConfig.storePassword==="your_store_password"||android.keystoreConfig.keyPassword==="your_key_password"||android.keystoreConfig.storePassword==="your_password"||android.keystoreConfig.keyPassword==="your_password"){progress.log(`Warning: Keystore contains placeholder passwords. Please update them.`,"warning");}else{progress.log(`Keystore configuration: Valid`,"success");}}else{progress.log(`Warning: Keystore configuration missing for AAB creation`,"warning");}}else{progress.log(`Signed AAB creation: Disabled (set createSignedAAB: true to enable)`,"info");}return{android,config};}async function diagnoseAndroidProject(projectPath){progress.log("Diagnosing Android project structure...","info");if(!_fs.default.existsSync(projectPath)){throw new Error(`Project path does not exist: ${projectPath}`);}// Check for common Android project patterns (supporting both .gradle and .gradle.kts)
34
- const patterns=[{files:["build.gradle","build.gradle.kts"],description:"Root build gradle",type:"either"},{files:["settings.gradle","settings.gradle.kts"],description:"Settings gradle",type:"either"},{files:["gradlew"],description:"Gradle wrapper script",type:"single"},{files:["gradle.properties"],description:"Gradle properties",type:"single"},{files:["app/build.gradle","app/build.gradle.kts"],description:"App module build gradle",type:"either"},{files:["app/src/main/AndroidManifest.xml"],description:"Android manifest",type:"single"},{files:["app/src/main/java"],description:"Java source directory",type:"single"},{files:["app/src/main/res"],description:"Android resources",type:"single"}];const diagnostics={foundFiles:[],missingFiles:[],projectType:"unknown",usesKotlinDSL:false};for(const pattern of patterns){let found=false;let foundFile=null;if(pattern.type==="either"){// Check if either variant exists
35
- for(const file of pattern.files){const fullPath=_path.default.join(projectPath,file);if(_fs.default.existsSync(fullPath)){found=true;foundFile=file;if(file.endsWith(".kts")){diagnostics.usesKotlinDSL=true;}break;}}}else{// Check single file
36
- const fullPath=_path.default.join(projectPath,pattern.files[0]);if(_fs.default.existsSync(fullPath)){found=true;foundFile=pattern.files[0];}}if(found){diagnostics.foundFiles.push({...pattern,foundFile});progress.log(`✅ Found: ${pattern.description}${foundFile?` (${foundFile})`:""}`,"success");}else{diagnostics.missingFiles.push(pattern);progress.log(`❌ Missing: ${pattern.description}`,"warning");}}// Determine project type
37
- const hasRootBuildFile=diagnostics.foundFiles.some(f=>f.files.includes("build.gradle")||f.files.includes("build.gradle.kts"));const hasAppBuildFile=diagnostics.foundFiles.some(f=>f.files.includes("app/build.gradle")||f.files.includes("app/build.gradle.kts"));const hasGradlew=diagnostics.foundFiles.some(f=>f.files.includes("gradlew"));if(hasRootBuildFile&&hasAppBuildFile){diagnostics.projectType="standard-android";}else if(hasGradlew){diagnostics.projectType="gradle-project";}else{diagnostics.projectType="incomplete";}progress.log(`Project type detected: ${diagnostics.projectType}`,"info");if(diagnostics.usesKotlinDSL){progress.log(`Project uses Kotlin DSL (.kts files)`,"info");}// Try to find build files in unexpected locations
38
- if(diagnostics.missingFiles.length>0){progress.log("Searching for missing files in alternate locations...","info");try{const findBuildFiles=`find "${projectPath}" \\( -name "build.gradle" -o -name "build.gradle.kts" -o -name "settings.gradle" -o -name "settings.gradle.kts" -o -name "AndroidManifest.xml" \\) -type f`;const foundFiles=(0,_utils.runCommand)(findBuildFiles);if(foundFiles.trim()){progress.log("Found build files in unexpected locations:","info");foundFiles.trim().split("\n").forEach(file=>{progress.log(` ${file}`,"info");});}}catch(searchError){progress.log("Could not search for alternative file locations","warning");}}return diagnostics;}async function validateProjectStructure(androidConfig){const{projectPath,oldProjectName}=androidConfig;progress.log("Validating project structure...","info");if(!_fs.default.existsSync(projectPath)){throw new Error(`Project path does not exist: ${projectPath}`);}// Run comprehensive diagnostics
39
- const diagnostics=await diagnoseAndroidProject(projectPath);if(diagnostics.projectType==="incomplete"){throw new Error(`Android project appears to be incomplete. Missing critical files: ${diagnostics.missingFiles.map(f=>f.description).join(", ")}`);}if(diagnostics.projectType!=="standard-android"){progress.log(`Warning: Project type "${diagnostics.projectType}" may not be fully compatible`,"warning");}// Check if the old project name exists in the path
40
- if(!projectPath.includes(oldProjectName)){progress.log(`Warning: Project path doesn't contain old project name "${oldProjectName}"`,"warning");}// Get the parent directory
41
- const parentDir=_path.default.dirname(projectPath);const currentProjectDir=_path.default.basename(projectPath);if(currentProjectDir!==oldProjectName){progress.log(`Warning: Current directory name "${currentProjectDir}" differs from old project name "${oldProjectName}"`,"warning");}// Create output directory path - where final AAB will be saved
42
- const outputPath=androidConfig.outputPath?_path.default.resolve(androidConfig.outputPath):_path.default.resolve(parentDir,"build-output");progress.log(`Original project: ${projectPath}`,"info");progress.log(`Output directory: ${outputPath}`,"info");progress.log("Project structure validation completed","success");return{parentDir,currentProjectDir,oldProjectPath:projectPath,outputPath,diagnostics};}async function createBackup(projectPaths,androidConfig){if(!androidConfig.createBackup){progress.log("Backup creation skipped (not requested)","info");return null;}progress.log("Creating project backup...","info");const backupPath=`${projectPaths.oldProjectPath}_backup_${Date.now()}`;try{;(0,_utils.runCommand)(`cp -r "${projectPaths.oldProjectPath}" "${backupPath}"`);progress.log(`Backup created at: ${backupPath}`,"success");return backupPath;}catch(error){throw new Error(`Failed to create backup: ${error.message}`);}}async function createTempDeploymentProject(projectPaths,androidConfig){const{oldProjectPath}=projectPaths;const{oldProjectName,newProjectName,packageName}=androidConfig;progress.log("Creating temporary deployment project...","info");// Create a temporary directory based on package name (if available) or timestamp
43
- const tempDirSuffix=packageName?packageName.replace(/\./g,"_"):Date.now().toString();const tempDir=_path.default.join(_path.default.dirname(oldProjectPath),`temp_build_${tempDirSuffix}`);// Remove existing temp directory with same name if it exists
44
- if(_fs.default.existsSync(tempDir)){progress.log(`Removing existing temp directory: ${tempDir}`,"info");try{;(0,_utils.runCommand)(`rm -rf "${tempDir}"`);}catch(cleanupError){progress.log(`Warning: Could not remove existing temp directory: ${cleanupError.message}`,"warning");}}try{// First, let's check what's actually in the source directory
45
- progress.log(`Inspecting source directory: ${oldProjectPath}`,"info");if(!_fs.default.existsSync(oldProjectPath)){throw new Error(`Source project path does not exist: ${oldProjectPath}`);}const sourceFiles=_fs.default.readdirSync(oldProjectPath);progress.log(`Source directory contents: ${sourceFiles.join(", ")}`,"info");// Copy the entire project to the temporary location using rsync for better file preservation
46
- progress.log(`Copying project to temporary location...`,"info");progress.log(`Source: ${oldProjectPath}`,"info");progress.log(`Temp destination: ${tempDir}`,"info")// Use rsync to preserve all file attributes and handle symlinks properly
47
- ;(0,_utils.runCommand)(`rsync -av "${oldProjectPath}/" "${tempDir}/"`);progress.log(`Created temporary copy for building`,"success");// Only check for gradlew which is absolutely essential for building
48
- const gradlewPath=_path.default.join(tempDir,"gradlew");if(!_fs.default.existsSync(gradlewPath)){throw new Error("Critical file missing: gradlew not found. This is required for building the project.");}// Log what we have (for debugging purposes only, not validation)
49
- progress.log(`Checking copied files (informational only):`,"info");const filesToCheck=[{path:"gradlew",desc:"Gradle wrapper"},{path:"build.gradle",desc:"Root build.gradle"},{path:"build.gradle.kts",desc:"Root build.gradle.kts"},{path:"app/build.gradle",desc:"App build.gradle"},{path:"app/build.gradle.kts",desc:"App build.gradle.kts"},{path:"settings.gradle",desc:"Settings gradle"},{path:"settings.gradle.kts",desc:"Settings gradle.kts"},{path:"app/src/main/AndroidManifest.xml",desc:"Android manifest"}];filesToCheck.forEach(file=>{const fullPath=_path.default.join(tempDir,file.path);const exists=_fs.default.existsSync(fullPath);progress.log(` ${file.desc}: ${exists?"✅ Found":"⚠️ Not found"}`,exists?"info":"warning");});// List all files in temp directory for debugging
50
- if(_fs.default.existsSync(tempDir)){const tempFiles=_fs.default.readdirSync(tempDir);progress.log(`Temp directory contents: ${tempFiles.join(", ")}`,"info");}}catch(error){// Clean up on error
51
- if(_fs.default.existsSync(tempDir)){try{;(0,_utils.runCommand)(`rm -rf "${tempDir}"`);}catch(cleanupError){progress.log(`Warning: Could not cleanup temp directory: ${cleanupError.message}`,"warning");}}throw new Error(`Failed to create temporary deployment copy: ${error.message}`);}// Find and rename any subdirectories that contain the old project name within the temp copy
52
- try{const findCommand=`find "${tempDir}" -type d -name "*${oldProjectName}*"`;const result=(0,_utils.runCommand)(findCommand);if(result.trim()){const dirsToRename=result.trim().split("\n").filter(dir=>dir.trim());for(const dir of dirsToRename){const newDirName=dir.split(oldProjectName).join(newProjectName);if(dir!==newDirName){;(0,_utils.runCommand)(`mv "${dir}" "${newDirName}"`);progress.log(`Renamed subdirectory: ${_path.default.basename(dir)} → ${_path.default.basename(newDirName)}`,"success");}}}}catch(error){progress.log(`Warning: Error finding subdirectories to rename: ${error.message}`,"warning");}progress.log("Temporary deployment project created successfully","success");return tempDir;}async function finalizeAABAndCleanup(projectPaths,androidConfig,aabResult,tempDeploymentPath){const{oldProjectPath}=projectPaths;progress.log("Finalizing AAB and cleaning up temporary files...","info");try{// Create final output directory next to original project
53
- const outputDir=androidConfig.outputPath||_path.default.join(_path.default.dirname(oldProjectPath),"build-output");if(!_fs.default.existsSync(outputDir)){_fs.default.mkdirSync(outputDir,{recursive:true});progress.log(`Created output directory: ${outputDir}`,"success");}// Copy AAB to final location with descriptive name
54
- const timestamp=new Date().toISOString().replace(/[:.]/g,"-").split("T")[0];const finalAabName=`${androidConfig.newProjectName}-release-${timestamp}.aab`;const finalAabPath=_path.default.join(outputDir,finalAabName);_fs.default.copyFileSync(aabResult.outputAabPath,finalAabPath);progress.log(`AAB saved to final location: ${finalAabPath}`,"success");// Copy keystore to output directory for future use
55
- const keystorePath=_path.default.join(tempDeploymentPath,"app","keystore.jks");if(_fs.default.existsSync(keystorePath)){const finalKeystorePath=_path.default.join(outputDir,`${androidConfig.newProjectName}-keystore.jks`);_fs.default.copyFileSync(keystorePath,finalKeystorePath);progress.log(`Keystore copied to: ${finalKeystorePath}`,"success");}// Clean up temporary deployment directory
56
- await cleanupTempDeployment(tempDeploymentPath);return{...aabResult,finalAabPath,finalAabName,outputDir};}catch(error){throw new Error(`Failed to finalize AAB: ${error.message}`);}}async function cleanupTempDeployment(tempDeploymentPath){if(!tempDeploymentPath||!_fs.default.existsSync(tempDeploymentPath)){return;}try{progress.log("Cleaning up temporary deployment files...","info");(0,_utils.runCommand)(`rm -rf "${tempDeploymentPath}"`);progress.log("Temporary files cleaned up successfully","success");}catch(error){progress.log(`Warning: Could not cleanup temporary directory: ${error.message}`,"warning");}}async function updateFileContents(projectPaths,androidConfig){const{newProjectPath}=projectPaths;const{oldProjectName,newProjectName,packageName}=androidConfig;progress.log("Updating file contents and names...","info");try{// STEP 1: Update packageName in build.gradle.kts and source files
57
- if(packageName){const OLD_PACKAGE="io.yourname.androidproject";progress.log(`Replacing package name: ${OLD_PACKAGE} → ${packageName}`,"info");// Update build.gradle.kts
58
- const buildGradlePath=_path.default.join(newProjectPath,"app","build.gradle.kts");if(_fs.default.existsSync(buildGradlePath)){let content=_fs.default.readFileSync(buildGradlePath,"utf8");content=content.replace(/namespace = "io\.yourname\.androidproject"/g,`namespace = "${packageName}"`);content=content.replace(/applicationId = "io\.yourname\.androidproject"/g,`applicationId = "${packageName}"`);_fs.default.writeFileSync(buildGradlePath,content,"utf8");progress.log(`✅ Updated build.gradle.kts with package: ${packageName}`,"success");}// Update source directory structure for all source sets
59
- const sourceSets=["main","test","noFcm","withFcm"];const newPackageParts=packageName.split(".");let totalUpdatedFiles=0;for(const sourceSet of sourceSets){const oldPackagePath=_path.default.join(newProjectPath,"app","src",sourceSet,"java","io","yourname","androidproject");if(_fs.default.existsSync(oldPackagePath)){const newPackagePath=_path.default.join(newProjectPath,"app","src",sourceSet,"java",...newPackageParts);const packagePathChanged=_path.default.resolve(oldPackagePath)!==_path.default.resolve(newPackagePath);if(packagePathChanged){// Create new package directory
60
- const newPackageDir=_path.default.dirname(newPackagePath);_fs.default.mkdirSync(newPackageDir,{recursive:true})// Move source files
61
- ;(0,_utils.runCommand)(`mv "${oldPackagePath}" "${newPackagePath}"`);progress.log(`✅ Moved ${sourceSet} source files to: ${newPackagePath}`,"success");}else{progress.log(`Package path unchanged for ${sourceSet}; skipping source directory move`,"info");}// Update package declarations in source files
62
- const findCommand=`find "${newPackagePath}" -type f \\( -name "*.kt" -o -name "*.java" \\)`;const sourceFiles=(0,_utils.runCommand)(findCommand).trim().split("\n").filter(f=>f.trim());let updatedCount=0;for(const file of sourceFiles){let content=_fs.default.readFileSync(file,"utf8");if(content.includes(OLD_PACKAGE)){content=content.replace(new RegExp(OLD_PACKAGE,"g"),packageName);_fs.default.writeFileSync(file,content,"utf8");updatedCount++;}}totalUpdatedFiles+=updatedCount;progress.log(`✅ Updated package declarations in ${updatedCount} ${sourceSet} files`,"success");// Clean up old empty directories
63
- try{;(0,_utils.runCommand)(`find "${_path.default.join(newProjectPath,"app","src",sourceSet,"java")}" -type d -empty -delete`);}catch(err){// Ignore cleanup errors
64
- }}}if(totalUpdatedFiles>0){progress.log(`✅ Total package declarations updated: ${totalUpdatedFiles} files`,"success");}// Update AndroidManifest.xml
65
- const manifestPath=_path.default.join(newProjectPath,"app","src","main","AndroidManifest.xml");if(_fs.default.existsSync(manifestPath)){let content=_fs.default.readFileSync(manifestPath,"utf8");if(content.includes(OLD_PACKAGE)){content=content.replace(new RegExp(OLD_PACKAGE,"g"),packageName);_fs.default.writeFileSync(manifestPath,content,"utf8");progress.log(`✅ Updated AndroidManifest.xml with package: ${packageName}`,"success");}}// Update proguard-rules.pro
66
- const proguardPath=_path.default.join(newProjectPath,"app","proguard-rules.pro");if(_fs.default.existsSync(proguardPath)){let content=_fs.default.readFileSync(proguardPath,"utf8");if(content.includes(OLD_PACKAGE)){content=content.replace(new RegExp(OLD_PACKAGE.replace(/\./g,"\\."),"g"),packageName);_fs.default.writeFileSync(proguardPath,content,"utf8");progress.log(`✅ Updated proguard-rules.pro with package: ${packageName}`,"success");}}}// STEP 2: Find and rename files that contain the old project name
67
- const findFilesCommand=`find "${newProjectPath}" -type f -name "*${oldProjectName}*"`;const fileResult=(0,_utils.runCommand)(findFilesCommand);if(fileResult.trim()){const filesToRename=fileResult.trim().split("\n").filter(file=>file.trim());for(const file of filesToRename){const newFileName=file.split(oldProjectName).join(newProjectName);if(file!==newFileName){;(0,_utils.runCommand)(`mv "${file}" "${newFileName}"`);progress.log(`Renamed file: ${_path.default.basename(file)} → ${_path.default.basename(newFileName)}`,"success");}}}// STEP 3: Update file contents that reference the old project name
68
- const fileTypes=["*.gradle","*.gradle.kts","*.xml","*.json","*.properties","*.java","*.kt","*.kts","*.js","*.ts","*.md"];for(const fileType of fileTypes){try{const findContentCommand=`find "${newProjectPath}" -name "${fileType}" -type f`;const files=(0,_utils.runCommand)(findContentCommand);if(files.trim()){const fileList=files.trim().split("\n").filter(file=>file.trim());for(const file of fileList){try{const content=_fs.default.readFileSync(file,"utf8");if(content.includes(oldProjectName)){const updatedContent=content.split(oldProjectName).join(newProjectName);_fs.default.writeFileSync(file,updatedContent,"utf8");progress.log(`Updated content in: ${_path.default.relative(newProjectPath,file)}`,"info");}}catch(fileError){progress.log(`Warning: Could not update file ${file}: ${fileError.message}`,"warning");}}}}catch(typeError){// Continue with other file types if one fails
69
- progress.log(`Warning: Error processing ${fileType} files: ${typeError.message}`,"warning");}}progress.log("File content updates completed","success");}catch(error){throw new Error(`Error updating file contents: ${error.message}`);}}async function buildAndroidAAB(configPathOrConfig){let androidConfig;let projectPaths;let backupPath;let tempDeploymentPath;try{// Initialize configuration - handle both config path and direct config object
70
- progress.start("config");if(typeof configPathOrConfig==="string"){const{android}=await initializeConfig(configPathOrConfig);androidConfig=android;}else{// Direct config object passed (from buildAppAndroid.js)
71
- // All defaults should already be applied in buildAppAndroid.js
72
- androidConfig=configPathOrConfig;// Validate required fields
73
- if(!androidConfig.newProjectName){throw new Error("newProjectName is required in android config");}if(!androidConfig.projectPath){throw new Error("projectPath is required in android config");}if(!androidConfig.oldProjectName){throw new Error("oldProjectName is required in android config");}progress.log(`Using configuration from buildAppAndroid.js`,"info");progress.log(`Project path: ${androidConfig.projectPath}`,"info");progress.log(`Old project name: ${androidConfig.oldProjectName}`,"info");progress.log(`New project name: ${androidConfig.newProjectName}`,"info");progress.log(`Deployment path: ${androidConfig.deploymentPath||"Not specified"}`,"info");progress.log(`Overwrite existing: ${androidConfig.overwriteExisting}`,"info");}progress.complete("config");// Validate project structure
74
- progress.start("validation");projectPaths=await validateProjectStructure(androidConfig);progress.complete("validation");// Create backup if requested
75
- progress.start("backup");backupPath=await createBackup(projectPaths,androidConfig);progress.complete("backup");// Create temporary deployment project copy
76
- progress.start("createTempDeployment");tempDeploymentPath=await createTempDeploymentProject(projectPaths,androidConfig);progress.complete("createTempDeployment");// Update file contents in temp deployment
77
- progress.start("updateFileContents");await updateFileContents({...projectPaths,newProjectPath:tempDeploymentPath},androidConfig);progress.complete("updateFileContents");// Create keystore if needed
78
- progress.start("createKeystore");const keystorePath=await createKeystore({...projectPaths,newProjectPath:tempDeploymentPath},androidConfig);progress.complete("createKeystore");// Create signed AAB if keystore was created/found and enabled in config
79
- let aabResult=null;if(keystorePath&&androidConfig.createSignedAAB){progress.start("createSignedAAB");aabResult=await createSignedAAB({...projectPaths,newProjectPath:tempDeploymentPath},androidConfig,keystorePath);progress.complete("createSignedAAB");// Copy AAB to final location and cleanup temp deployment
80
- if(aabResult){progress.start("finalizeAAB");aabResult=await finalizeAABAndCleanup(projectPaths,androidConfig,aabResult,tempDeploymentPath);progress.complete("finalizeAAB");}}else if(!androidConfig.createSignedAAB){progress.log("Signed AAB creation skipped (not enabled in config)","info");// DON'T Clean up temp deployment - keep it for verification
81
- progress.log(`Temp renamed project kept at: ${tempDeploymentPath}`,"info");// await cleanupTempDeployment(tempDeploymentPath)
82
- }else if(!keystorePath){progress.log("Signed AAB creation skipped (no keystore available)","warning");// DON'T Clean up temp deployment - keep it for verification
83
- progress.log(`Temp renamed project kept at: ${tempDeploymentPath}`,"info");// await cleanupTempDeployment(tempDeploymentPath)
84
- }// Print completion summary
85
- progress.printTreeContent("Build Summary",["Android AAB build completed successfully:",{text:`Original project: ${projectPaths.oldProjectPath}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Project name: ${androidConfig.oldProjectName} → ${androidConfig.newProjectName}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Deployment path: ${androidConfig.deploymentPath||"Not specified"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Overwrite existing: ${androidConfig.overwriteExisting}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Backup created: ${backupPath?"Yes":"No"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Keystore: ${keystorePath?"Created/Verified":"Skipped"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Signed AAB: ${aabResult?"Created":"Skipped"}`,indent:1,prefix:"└─ ",color:"gray"},"","Output Files:",{text:`Original preserved at: ${projectPaths.oldProjectPath}`,indent:1,prefix:"├─ ",color:"green"},...(backupPath?[{text:`Backup: ${backupPath}`,indent:1,prefix:"├─ ",color:"gray"}]:[]),...(aabResult?[{text:`AAB file: ${aabResult.finalAabPath}`,indent:1,prefix:"├─ ",color:"green"},{text:`AAB size: ${aabResult.fileSize} MB`,indent:1,prefix:"└─ ",color:"green"}]:[{text:`No AAB created`,indent:1,prefix:"└─ ",color:"gray"}])]);process.exit(0);}catch(error){// Clean up temp deployment on error
86
- if(tempDeploymentPath){await cleanupTempDeployment(tempDeploymentPath);}if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);progress.printTreeContent("Troubleshooting Guide",["Build operation failed. Please check the following:",{text:"Verify config file exists and contains required android configuration",indent:1,prefix:"├─ ",color:"yellow"},{text:"Ensure project path exists and is accessible",indent:1,prefix:"├─ ",color:"yellow"},{text:"Check file/directory permissions",indent:1,prefix:"├─ ",color:"yellow"},{text:"Verify no processes are using the project directory",indent:1,prefix:"├─ ",color:"yellow"},{text:"Ensure sufficient disk space for project copy",indent:1,prefix:"├─ ",color:"yellow"},{text:'Update keystore passwords (do not use "your_store_password")',indent:1,prefix:"└─ ",color:"yellow"},"\nConfiguration Details:",{text:`Config: ${typeof configPathOrConfig==="string"?configPathOrConfig:"Direct object from buildAppAndroid.js"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Project path: ${androidConfig?.projectPath||"Not loaded"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Deployment path: ${androidConfig?.deploymentPath||"Not loaded"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Old project name: ${androidConfig?.oldProjectName||"Not loaded"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`New project name: ${androidConfig?.newProjectName||"Not loaded"}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Overwrite existing: ${androidConfig?.overwriteExisting!==undefined?androidConfig.overwriteExisting:"Not loaded"}`,indent:1,prefix:"└─ ",color:"gray"},"\nFor keystore issues, ensure your config has:",{text:"Real passwords (not placeholder values)",indent:1,prefix:"├─ ",color:"cyan"},{text:"Valid organization information",indent:1,prefix:"├─ ",color:"cyan"},{text:"Consistent store and key passwords for PKCS12",indent:1,prefix:"└─ ",color:"cyan"}]);}process.exit(1);}}// Export functions for use as a module - using the same export pattern as buildAppAndroid.js
1
+ /* eslint-disable no-extra-semi */
2
+ "use strict";
3
+
4
+ var _fs = _interopRequireDefault(require("fs"));
5
+ var _path = _interopRequireDefault(require("path"));
6
+ var _utils = require("./utils.js");
7
+ function _interopRequireDefault(e) {
8
+ return e && e.__esModule ? e : {
9
+ default: e
10
+ };
11
+ }
12
+
13
+ // Simple progress logging without TerminalProgress dependency
14
+ class SimpleProgress {
15
+ constructor(title) {
16
+ this.title = title;
17
+ this.currentStep = null;
18
+ console.log(`\n=== ${title} ===\n`);
19
+ }
20
+ start(stepId) {
21
+ this.currentStep = {
22
+ id: stepId
23
+ };
24
+ console.log(`⏳ Starting: ${stepId}`);
25
+ }
26
+ complete(stepId) {
27
+ console.log(`✅ Completed: ${stepId}`);
28
+ this.currentStep = null;
29
+ }
30
+ fail(stepId, message) {
31
+ console.log(`❌ Failed: ${stepId} - ${message}`);
32
+ }
33
+ log(message, type = "info") {
34
+ const prefix = type === "error" ? "❌" : type === "warning" ? "⚠️ " : type === "success" ? "✅" : "ℹ️ ";
35
+ console.log(` ${prefix} ${message}`);
36
+ }
37
+ printTreeContent(title, items) {
38
+ console.log(`\n📋 ${title}:`);
39
+ items.forEach(item => {
40
+ if (typeof item === "string") {
41
+ console.log(item);
42
+ } else {
43
+ const indent = " ".repeat(item.indent || 0);
44
+ const prefix = item.prefix || "";
45
+ console.log(`${indent}${prefix}${item.text}`);
46
+ }
47
+ });
48
+ console.log("");
49
+ }
50
+ }
51
+ const progress = new SimpleProgress("Android AAB Builder");
52
+ async function createKeystore(projectPaths, androidConfig) {
53
+ const {
54
+ newProjectPath
55
+ } = projectPaths;
56
+ const {
57
+ keystoreConfig
58
+ } = androidConfig;
59
+ if (!keystoreConfig) {
60
+ progress.log("Keystore configuration not found, skipping keystore creation", "info");
61
+ return null;
62
+ }
63
+ progress.log("Checking keystore configuration...", "info");
64
+
65
+ // Validate keystore passwords are not placeholder values
66
+ if (keystoreConfig.storePassword === "your_store_password" || keystoreConfig.keyPassword === "your_key_password" || keystoreConfig.storePassword === "your_password" || keystoreConfig.keyPassword === "your_password") {
67
+ throw new Error('Please update keystore passwords in config file. Do not use placeholder values like "your_store_password" or "your_key_password".');
68
+ }
69
+
70
+ // Define keystore path
71
+ const keystorePath = _path.default.join(newProjectPath, "app", "keystore.jks");
72
+
73
+ // Check if keystore already exists
74
+ if (_fs.default.existsSync(keystorePath)) {
75
+ progress.log("Keystore already exists, verifying accessibility...", "info");
76
+
77
+ // Test keystore accessibility
78
+ try {
79
+ const testCommand = `keytool -list -keystore "${keystorePath}" -storepass "${keystoreConfig.storePassword}" -alias "${keystoreConfig.keyAlias}"`;
80
+ (0, _utils.runCommand)(testCommand);
81
+ progress.log("Existing keystore verified successfully", "success");
82
+ return keystorePath;
83
+ } catch (testError) {
84
+ progress.log("Existing keystore verification failed, creating new one...", "warning");
85
+ // Remove the faulty keystore
86
+ _fs.default.unlinkSync(keystorePath);
87
+ }
88
+ }
89
+ progress.log("Creating new keystore...", "info");
90
+ try {
91
+ // Validate required keystore configuration
92
+ const requiredFields = ["keyAlias", "storePassword", "keyPassword", "organizationInfo"];
93
+ for (const field of requiredFields) {
94
+ if (!keystoreConfig[field]) {
95
+ throw new Error(`Missing required keystore config field: ${field}`);
96
+ }
97
+ }
98
+ const {
99
+ organizationInfo
100
+ } = keystoreConfig;
101
+ const requiredOrgFields = ["companyName", "city", "state", "countryCode"];
102
+ for (const field of requiredOrgFields) {
103
+ if (!organizationInfo[field]) {
104
+ throw new Error(`Missing required organization info field: ${field}`);
105
+ }
106
+ }
107
+
108
+ // Create keystore directory if it doesn't exist
109
+ const keystoreDir = _path.default.dirname(keystorePath);
110
+ if (!_fs.default.existsSync(keystoreDir)) {
111
+ _fs.default.mkdirSync(keystoreDir, {
112
+ recursive: true
113
+ });
114
+ }
115
+
116
+ // Build the distinguished name (DN) for the certificate
117
+ const dn = [`CN=${organizationInfo.companyName}`, `OU=${organizationInfo.department || organizationInfo.companyName}`, `O=${organizationInfo.organization || organizationInfo.companyName}`, `L=${organizationInfo.city}`, `ST=${organizationInfo.state}`, `C=${organizationInfo.countryCode}`].join(", ");
118
+
119
+ // For PKCS12 keystores (default), store and key passwords must be the same
120
+ // Use store password for both to avoid PKCS12 compatibility issues
121
+ const effectiveKeyPassword = keystoreConfig.storePassword;
122
+ if (keystoreConfig.keyPassword !== keystoreConfig.storePassword) {
123
+ progress.log("Note: Using store password for key password (PKCS12 requirement)", "info");
124
+ }
125
+
126
+ // Generate keystore using keytool with PKCS12 format explicitly
127
+ const validityDays = (keystoreConfig.validityYears || 25) * 365;
128
+ const keystoreCommand = ["keytool", "-genkeypair", "-v", `-storetype PKCS12`, `-keystore "${keystorePath}"`, `-alias "${keystoreConfig.keyAlias}"`, `-keyalg RSA`, `-keysize 2048`, `-validity ${validityDays}`, `-storepass "${keystoreConfig.storePassword}"`, `-keypass "${effectiveKeyPassword}"`, `-dname "${dn}"`].join(" ");
129
+ progress.log("Executing keytool command...", "info");
130
+ (0, _utils.runCommand)(keystoreCommand);
131
+
132
+ // Verify keystore was created
133
+ if (!_fs.default.existsSync(keystorePath)) {
134
+ throw new Error("Keystore file was not created successfully");
135
+ }
136
+
137
+ // Test the newly created keystore
138
+ try {
139
+ const testCommand = `keytool -list -keystore "${keystorePath}" -storepass "${keystoreConfig.storePassword}" -alias "${keystoreConfig.keyAlias}"`;
140
+ (0, _utils.runCommand)(testCommand);
141
+ progress.log("New keystore verified successfully", "success");
142
+ } catch (testError) {
143
+ throw new Error(`Created keystore failed verification: ${testError.message}`);
144
+ }
145
+ progress.log(`Keystore created successfully at: ${keystorePath}`, "success");
146
+ progress.log(`Key alias: ${keystoreConfig.keyAlias}`, "info");
147
+ progress.log(`Validity: ${keystoreConfig.validityYears || 25} years`, "info");
148
+ progress.log(`Store type: PKCS12`, "info");
149
+ return keystorePath;
150
+ } catch (error) {
151
+ throw new Error(`Failed to create keystore: ${error.message}`);
152
+ }
153
+ }
154
+ async function createSignedAAB(projectPaths, androidConfig, keystorePath) {
155
+ const {
156
+ newProjectPath
157
+ } = projectPaths;
158
+ const {
159
+ keystoreConfig
160
+ } = androidConfig;
161
+ if (!keystoreConfig || !keystorePath) {
162
+ throw new Error("Keystore configuration and keystore path are required for signed AAB creation");
163
+ }
164
+ progress.log("Building signed Android App Bundle (AAB)...", "info");
165
+ try {
166
+ // Verify keystore exists
167
+ if (!_fs.default.existsSync(keystorePath)) {
168
+ throw new Error(`Keystore not found at: ${keystorePath}`);
169
+ }
170
+
171
+ // Get absolute path for the project directory
172
+ const absoluteProjectPath = _path.default.resolve(newProjectPath);
173
+
174
+ // Change to project directory
175
+ const originalCwd = process.cwd();
176
+ process.chdir(absoluteProjectPath);
177
+ progress.log(`Changed to project directory: ${absoluteProjectPath}`, "info");
178
+
179
+ // Verify gradlew exists using absolute path
180
+ const gradlewPath = _path.default.join(absoluteProjectPath, "gradlew");
181
+ progress.log(`Looking for gradlew at: ${gradlewPath}`, "info");
182
+ if (!_fs.default.existsSync(gradlewPath)) {
183
+ // List files in the project directory for debugging
184
+ try {
185
+ const files = _fs.default.readdirSync(absoluteProjectPath);
186
+ progress.log(`Files in project directory: ${files.join(", ")}`, "info");
187
+
188
+ // Check if there's a gradlew file with different permissions or name
189
+ const gradlewFiles = files.filter(file => file.toLowerCase().includes("gradle"));
190
+ if (gradlewFiles.length > 0) {
191
+ progress.log(`Found gradle-related files: ${gradlewFiles.join(", ")}`, "info");
192
+ }
193
+ } catch (listError) {
194
+ progress.log(`Could not list directory contents: ${listError.message}`, "warning");
195
+ }
196
+ throw new Error(`gradlew not found at: ${gradlewPath}. Ensure this is a valid Android project.`);
197
+ }
198
+
199
+ // Make gradlew executable
200
+ try {
201
+ ;
202
+ (0, _utils.runCommand)(`chmod +x ./gradlew`);
203
+ } catch (chmodError) {
204
+ progress.log(`Warning: Could not make gradlew executable: ${chmodError.message}`, "warning");
205
+ }
206
+ try {
207
+ // Clean previous builds
208
+ progress.log("Cleaning previous builds...", "info");
209
+ try {
210
+ ;
211
+ (0, _utils.runCommand)("./gradlew clean");
212
+ progress.log("Clean completed successfully", "success");
213
+ } catch (cleanError) {
214
+ progress.log(`Warning: Clean failed: ${cleanError.message}`, "warning");
215
+ // Continue anyway as this might not be critical
216
+ }
217
+
218
+ // Build signed AAB with PKCS12-compatible parameters
219
+ progress.log("Building signed AAB...", "info");
220
+
221
+ // For PKCS12 keystores, use store password for both store and key
222
+ const effectiveKeyPassword = keystoreConfig.storePassword;
223
+ const bundleCommand = ["./gradlew bundleRelease --quiet --console=rich", `-Pandroid.injected.signing.store.file="${keystorePath}"`, `-Pandroid.injected.signing.store.password="${keystoreConfig.storePassword}"`, `-Pandroid.injected.signing.key.alias="${keystoreConfig.keyAlias}"`, `-Pandroid.injected.signing.key.password="${effectiveKeyPassword}"`].join(" ");
224
+ progress.log(`Executing: ${bundleCommand}`, "info");
225
+ (0, _utils.runCommand)(bundleCommand);
226
+ progress.log("Bundle build completed", "success");
227
+
228
+ // Find the generated AAB file
229
+ const aabPath = _path.default.join(newProjectPath, "app", "build", "outputs", "bundle", "release", "app-release.aab");
230
+ progress.log(`Looking for AAB file at: ${aabPath}`, "info");
231
+ if (!_fs.default.existsSync(aabPath)) {
232
+ // Try to find AAB files in the build output directory
233
+ const bundleDir = _path.default.join(newProjectPath, "app", "build", "outputs", "bundle");
234
+ progress.log(`AAB not found at expected location. Checking bundle directory: ${bundleDir}`, "warning");
235
+ if (_fs.default.existsSync(bundleDir)) {
236
+ try {
237
+ const findAabCommand = `find "${bundleDir}" -name "*.aab" -type f`;
238
+ const foundAabs = (0, _utils.runCommand)(findAabCommand);
239
+ if (foundAabs.trim()) {
240
+ progress.log(`Found AAB files: ${foundAabs.trim()}`, "info");
241
+ } else {
242
+ progress.log("No AAB files found in bundle directory", "warning");
243
+ }
244
+ } catch (findError) {
245
+ progress.log(`Error searching for AAB files: ${findError.message}`, "warning");
246
+ }
247
+ }
248
+ throw new Error("Signed AAB file was not generated successfully");
249
+ }
250
+
251
+ // Get file size
252
+ const stats = _fs.default.statSync(aabPath);
253
+ const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(2);
254
+ progress.log(`Signed AAB created successfully!`, "success");
255
+ progress.log(`AAB location: ${aabPath}`, "info");
256
+ progress.log(`AAB size: ${fileSizeMB} MB`, "info");
257
+
258
+ // Optionally copy AAB to a more accessible location
259
+ const outputDir = _path.default.join(newProjectPath, "release");
260
+ if (!_fs.default.existsSync(outputDir)) {
261
+ _fs.default.mkdirSync(outputDir, {
262
+ recursive: true
263
+ });
264
+ }
265
+ const outputAabPath = _path.default.join(outputDir, `${androidConfig.newProjectName}-release.aab`);
266
+ _fs.default.copyFileSync(aabPath, outputAabPath);
267
+ progress.log(`AAB copied to: ${outputAabPath}`, "success");
268
+ return {
269
+ aabPath,
270
+ outputAabPath,
271
+ fileSize: fileSizeMB
272
+ };
273
+ } finally {
274
+ // Restore original working directory
275
+ process.chdir(originalCwd);
276
+ }
277
+ } catch (error) {
278
+ throw new Error(`Failed to create signed AAB: ${error.message}`);
279
+ }
280
+ }
281
+ async function initializeConfig(configPath) {
282
+ if (!configPath) {
283
+ throw new Error("Config path is required");
284
+ }
285
+ if (!_fs.default.existsSync(configPath)) {
286
+ throw new Error(`Config file not found at: ${configPath}`);
287
+ }
288
+ progress.log(`Reading config from: ${configPath}`, "info");
289
+ const configFile = _fs.default.readFileSync(configPath, "utf8");
290
+ const config = JSON.parse(configFile);
291
+ if (!config.android) {
292
+ throw new Error("Android configuration missing in config file");
293
+ }
294
+ const {
295
+ android
296
+ } = config;
297
+
298
+ // Validate required fields (only newProjectName is truly required)
299
+ if (!android.newProjectName) {
300
+ throw new Error("newProjectName is required in android config");
301
+ }
302
+ progress.log(`Project path: ${android.projectPath}`, "info");
303
+ progress.log(`Old project name: ${android.oldProjectName}`, "info");
304
+ progress.log(`New project name: ${android.newProjectName}`, "info");
305
+ if (android.outputPath) {
306
+ progress.log(`Custom output path: ${android.outputPath}`, "info");
307
+ } else {
308
+ const parentDir = _path.default.dirname(android.projectPath);
309
+ const defaultOutputPath = _path.default.join(parentDir, "build-output");
310
+ progress.log(`Default output path: ${defaultOutputPath}`, "info");
311
+ }
312
+ if (android.createSignedAAB) {
313
+ progress.log(`Signed AAB creation: Enabled`, "info");
314
+ if (android.keystoreConfig) {
315
+ // Check for placeholder passwords
316
+ if (android.keystoreConfig.storePassword === "your_store_password" || android.keystoreConfig.keyPassword === "your_key_password" || android.keystoreConfig.storePassword === "your_password" || android.keystoreConfig.keyPassword === "your_password") {
317
+ progress.log(`Warning: Keystore contains placeholder passwords. Please update them.`, "warning");
318
+ } else {
319
+ progress.log(`Keystore configuration: Valid`, "success");
320
+ }
321
+ } else {
322
+ progress.log(`Warning: Keystore configuration missing for AAB creation`, "warning");
323
+ }
324
+ } else {
325
+ progress.log(`Signed AAB creation: Disabled (set createSignedAAB: true to enable)`, "info");
326
+ }
327
+ return {
328
+ android,
329
+ config
330
+ };
331
+ }
332
+ async function diagnoseAndroidProject(projectPath) {
333
+ progress.log("Diagnosing Android project structure...", "info");
334
+ if (!_fs.default.existsSync(projectPath)) {
335
+ throw new Error(`Project path does not exist: ${projectPath}`);
336
+ }
337
+
338
+ // Check for common Android project patterns (supporting both .gradle and .gradle.kts)
339
+ const patterns = [{
340
+ files: ["build.gradle", "build.gradle.kts"],
341
+ description: "Root build gradle",
342
+ type: "either"
343
+ }, {
344
+ files: ["settings.gradle", "settings.gradle.kts"],
345
+ description: "Settings gradle",
346
+ type: "either"
347
+ }, {
348
+ files: ["gradlew"],
349
+ description: "Gradle wrapper script",
350
+ type: "single"
351
+ }, {
352
+ files: ["gradle.properties"],
353
+ description: "Gradle properties",
354
+ type: "single"
355
+ }, {
356
+ files: ["app/build.gradle", "app/build.gradle.kts"],
357
+ description: "App module build gradle",
358
+ type: "either"
359
+ }, {
360
+ files: ["app/src/main/AndroidManifest.xml"],
361
+ description: "Android manifest",
362
+ type: "single"
363
+ }, {
364
+ files: ["app/src/main/java"],
365
+ description: "Java source directory",
366
+ type: "single"
367
+ }, {
368
+ files: ["app/src/main/res"],
369
+ description: "Android resources",
370
+ type: "single"
371
+ }];
372
+ const diagnostics = {
373
+ foundFiles: [],
374
+ missingFiles: [],
375
+ projectType: "unknown",
376
+ usesKotlinDSL: false
377
+ };
378
+ for (const pattern of patterns) {
379
+ let found = false;
380
+ let foundFile = null;
381
+ if (pattern.type === "either") {
382
+ // Check if either variant exists
383
+ for (const file of pattern.files) {
384
+ const fullPath = _path.default.join(projectPath, file);
385
+ if (_fs.default.existsSync(fullPath)) {
386
+ found = true;
387
+ foundFile = file;
388
+ if (file.endsWith(".kts")) {
389
+ diagnostics.usesKotlinDSL = true;
390
+ }
391
+ break;
392
+ }
393
+ }
394
+ } else {
395
+ // Check single file
396
+ const fullPath = _path.default.join(projectPath, pattern.files[0]);
397
+ if (_fs.default.existsSync(fullPath)) {
398
+ found = true;
399
+ foundFile = pattern.files[0];
400
+ }
401
+ }
402
+ if (found) {
403
+ diagnostics.foundFiles.push({
404
+ ...pattern,
405
+ foundFile
406
+ });
407
+ progress.log(`✅ Found: ${pattern.description}${foundFile ? ` (${foundFile})` : ""}`, "success");
408
+ } else {
409
+ diagnostics.missingFiles.push(pattern);
410
+ progress.log(`❌ Missing: ${pattern.description}`, "warning");
411
+ }
412
+ }
413
+
414
+ // Determine project type
415
+ const hasRootBuildFile = diagnostics.foundFiles.some(f => f.files.includes("build.gradle") || f.files.includes("build.gradle.kts"));
416
+ const hasAppBuildFile = diagnostics.foundFiles.some(f => f.files.includes("app/build.gradle") || f.files.includes("app/build.gradle.kts"));
417
+ const hasGradlew = diagnostics.foundFiles.some(f => f.files.includes("gradlew"));
418
+ if (hasRootBuildFile && hasAppBuildFile) {
419
+ diagnostics.projectType = "standard-android";
420
+ } else if (hasGradlew) {
421
+ diagnostics.projectType = "gradle-project";
422
+ } else {
423
+ diagnostics.projectType = "incomplete";
424
+ }
425
+ progress.log(`Project type detected: ${diagnostics.projectType}`, "info");
426
+ if (diagnostics.usesKotlinDSL) {
427
+ progress.log(`Project uses Kotlin DSL (.kts files)`, "info");
428
+ }
429
+
430
+ // Try to find build files in unexpected locations
431
+ if (diagnostics.missingFiles.length > 0) {
432
+ progress.log("Searching for missing files in alternate locations...", "info");
433
+ try {
434
+ const findBuildFiles = `find "${projectPath}" \\( -name "build.gradle" -o -name "build.gradle.kts" -o -name "settings.gradle" -o -name "settings.gradle.kts" -o -name "AndroidManifest.xml" \\) -type f`;
435
+ const foundFiles = (0, _utils.runCommand)(findBuildFiles);
436
+ if (foundFiles.trim()) {
437
+ progress.log("Found build files in unexpected locations:", "info");
438
+ foundFiles.trim().split("\n").forEach(file => {
439
+ progress.log(` ${file}`, "info");
440
+ });
441
+ }
442
+ } catch (searchError) {
443
+ progress.log("Could not search for alternative file locations", "warning");
444
+ }
445
+ }
446
+ return diagnostics;
447
+ }
448
+ async function validateProjectStructure(androidConfig) {
449
+ const {
450
+ projectPath,
451
+ oldProjectName
452
+ } = androidConfig;
453
+ progress.log("Validating project structure...", "info");
454
+ if (!_fs.default.existsSync(projectPath)) {
455
+ throw new Error(`Project path does not exist: ${projectPath}`);
456
+ }
457
+
458
+ // Run comprehensive diagnostics
459
+ const diagnostics = await diagnoseAndroidProject(projectPath);
460
+ if (diagnostics.projectType === "incomplete") {
461
+ throw new Error(`Android project appears to be incomplete. Missing critical files: ${diagnostics.missingFiles.map(f => f.description).join(", ")}`);
462
+ }
463
+ if (diagnostics.projectType !== "standard-android") {
464
+ progress.log(`Warning: Project type "${diagnostics.projectType}" may not be fully compatible`, "warning");
465
+ }
466
+
467
+ // Check if the old project name exists in the path
468
+ if (!projectPath.includes(oldProjectName)) {
469
+ progress.log(`Warning: Project path doesn't contain old project name "${oldProjectName}"`, "warning");
470
+ }
471
+
472
+ // Get the parent directory
473
+ const parentDir = _path.default.dirname(projectPath);
474
+ const currentProjectDir = _path.default.basename(projectPath);
475
+ if (currentProjectDir !== oldProjectName) {
476
+ progress.log(`Warning: Current directory name "${currentProjectDir}" differs from old project name "${oldProjectName}"`, "warning");
477
+ }
478
+
479
+ // Create output directory path - where final AAB will be saved
480
+ const outputPath = androidConfig.outputPath ? _path.default.resolve(androidConfig.outputPath) : _path.default.resolve(parentDir, "build-output");
481
+ progress.log(`Original project: ${projectPath}`, "info");
482
+ progress.log(`Output directory: ${outputPath}`, "info");
483
+ progress.log("Project structure validation completed", "success");
484
+ return {
485
+ parentDir,
486
+ currentProjectDir,
487
+ oldProjectPath: projectPath,
488
+ outputPath,
489
+ diagnostics
490
+ };
491
+ }
492
+ async function createBackup(projectPaths, androidConfig) {
493
+ if (!androidConfig.createBackup) {
494
+ progress.log("Backup creation skipped (not requested)", "info");
495
+ return null;
496
+ }
497
+ progress.log("Creating project backup...", "info");
498
+ const backupPath = `${projectPaths.oldProjectPath}_backup_${Date.now()}`;
499
+ try {
500
+ ;
501
+ (0, _utils.runCommand)(`cp -r "${projectPaths.oldProjectPath}" "${backupPath}"`);
502
+ progress.log(`Backup created at: ${backupPath}`, "success");
503
+ return backupPath;
504
+ } catch (error) {
505
+ throw new Error(`Failed to create backup: ${error.message}`);
506
+ }
507
+ }
508
+ async function createTempDeploymentProject(projectPaths, androidConfig) {
509
+ const {
510
+ oldProjectPath
511
+ } = projectPaths;
512
+ const {
513
+ oldProjectName,
514
+ newProjectName,
515
+ packageName
516
+ } = androidConfig;
517
+ progress.log("Creating temporary deployment project...", "info");
518
+
519
+ // Create a temporary directory based on package name (if available) or timestamp
520
+ const tempDirSuffix = packageName ? packageName.replace(/\./g, "_") : Date.now().toString();
521
+ const tempDir = _path.default.join(_path.default.dirname(oldProjectPath), `temp_build_${tempDirSuffix}`);
522
+
523
+ // Remove existing temp directory with same name if it exists
524
+ if (_fs.default.existsSync(tempDir)) {
525
+ progress.log(`Removing existing temp directory: ${tempDir}`, "info");
526
+ try {
527
+ ;
528
+ (0, _utils.runCommand)(`rm -rf "${tempDir}"`);
529
+ } catch (cleanupError) {
530
+ progress.log(`Warning: Could not remove existing temp directory: ${cleanupError.message}`, "warning");
531
+ }
532
+ }
533
+ try {
534
+ // First, let's check what's actually in the source directory
535
+ progress.log(`Inspecting source directory: ${oldProjectPath}`, "info");
536
+ if (!_fs.default.existsSync(oldProjectPath)) {
537
+ throw new Error(`Source project path does not exist: ${oldProjectPath}`);
538
+ }
539
+ const sourceFiles = _fs.default.readdirSync(oldProjectPath);
540
+ progress.log(`Source directory contents: ${sourceFiles.join(", ")}`, "info");
541
+
542
+ // Copy the entire project to the temporary location using rsync for better file preservation
543
+ progress.log(`Copying project to temporary location...`, "info");
544
+ progress.log(`Source: ${oldProjectPath}`, "info");
545
+ progress.log(`Temp destination: ${tempDir}`, "info")
546
+
547
+ // Use rsync to preserve all file attributes and handle symlinks properly
548
+ ;
549
+ (0, _utils.runCommand)(`rsync -av "${oldProjectPath}/" "${tempDir}/"`);
550
+ progress.log(`Created temporary copy for building`, "success");
551
+
552
+ // Only check for gradlew which is absolutely essential for building
553
+ const gradlewPath = _path.default.join(tempDir, "gradlew");
554
+ if (!_fs.default.existsSync(gradlewPath)) {
555
+ throw new Error("Critical file missing: gradlew not found. This is required for building the project.");
556
+ }
557
+
558
+ // Log what we have (for debugging purposes only, not validation)
559
+ progress.log(`Checking copied files (informational only):`, "info");
560
+ const filesToCheck = [{
561
+ path: "gradlew",
562
+ desc: "Gradle wrapper"
563
+ }, {
564
+ path: "build.gradle",
565
+ desc: "Root build.gradle"
566
+ }, {
567
+ path: "build.gradle.kts",
568
+ desc: "Root build.gradle.kts"
569
+ }, {
570
+ path: "app/build.gradle",
571
+ desc: "App build.gradle"
572
+ }, {
573
+ path: "app/build.gradle.kts",
574
+ desc: "App build.gradle.kts"
575
+ }, {
576
+ path: "settings.gradle",
577
+ desc: "Settings gradle"
578
+ }, {
579
+ path: "settings.gradle.kts",
580
+ desc: "Settings gradle.kts"
581
+ }, {
582
+ path: "app/src/main/AndroidManifest.xml",
583
+ desc: "Android manifest"
584
+ }];
585
+ filesToCheck.forEach(file => {
586
+ const fullPath = _path.default.join(tempDir, file.path);
587
+ const exists = _fs.default.existsSync(fullPath);
588
+ progress.log(` ${file.desc}: ${exists ? "✅ Found" : "⚠️ Not found"}`, exists ? "info" : "warning");
589
+ });
590
+
591
+ // List all files in temp directory for debugging
592
+ if (_fs.default.existsSync(tempDir)) {
593
+ const tempFiles = _fs.default.readdirSync(tempDir);
594
+ progress.log(`Temp directory contents: ${tempFiles.join(", ")}`, "info");
595
+ }
596
+ } catch (error) {
597
+ // Clean up on error
598
+ if (_fs.default.existsSync(tempDir)) {
599
+ try {
600
+ ;
601
+ (0, _utils.runCommand)(`rm -rf "${tempDir}"`);
602
+ } catch (cleanupError) {
603
+ progress.log(`Warning: Could not cleanup temp directory: ${cleanupError.message}`, "warning");
604
+ }
605
+ }
606
+ throw new Error(`Failed to create temporary deployment copy: ${error.message}`);
607
+ }
608
+
609
+ // Find and rename any subdirectories that contain the old project name within the temp copy
610
+ try {
611
+ const findCommand = `find "${tempDir}" -type d -name "*${oldProjectName}*"`;
612
+ const result = (0, _utils.runCommand)(findCommand);
613
+ if (result.trim()) {
614
+ const dirsToRename = result.trim().split("\n").filter(dir => dir.trim());
615
+ for (const dir of dirsToRename) {
616
+ const newDirName = dir.split(oldProjectName).join(newProjectName);
617
+ if (dir !== newDirName) {
618
+ ;
619
+ (0, _utils.runCommand)(`mv "${dir}" "${newDirName}"`);
620
+ progress.log(`Renamed subdirectory: ${_path.default.basename(dir)} → ${_path.default.basename(newDirName)}`, "success");
621
+ }
622
+ }
623
+ }
624
+ } catch (error) {
625
+ progress.log(`Warning: Error finding subdirectories to rename: ${error.message}`, "warning");
626
+ }
627
+ progress.log("Temporary deployment project created successfully", "success");
628
+ return tempDir;
629
+ }
630
+ async function finalizeAABAndCleanup(projectPaths, androidConfig, aabResult, tempDeploymentPath) {
631
+ const {
632
+ oldProjectPath
633
+ } = projectPaths;
634
+ progress.log("Finalizing AAB and cleaning up temporary files...", "info");
635
+ try {
636
+ // Create final output directory next to original project
637
+ const outputDir = androidConfig.outputPath || _path.default.join(_path.default.dirname(oldProjectPath), "build-output");
638
+ if (!_fs.default.existsSync(outputDir)) {
639
+ _fs.default.mkdirSync(outputDir, {
640
+ recursive: true
641
+ });
642
+ progress.log(`Created output directory: ${outputDir}`, "success");
643
+ }
644
+
645
+ // Copy AAB to final location with descriptive name
646
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").split("T")[0];
647
+ const finalAabName = `${androidConfig.newProjectName}-release-${timestamp}.aab`;
648
+ const finalAabPath = _path.default.join(outputDir, finalAabName);
649
+ _fs.default.copyFileSync(aabResult.outputAabPath, finalAabPath);
650
+ progress.log(`AAB saved to final location: ${finalAabPath}`, "success");
651
+
652
+ // Copy keystore to output directory for future use
653
+ const keystorePath = _path.default.join(tempDeploymentPath, "app", "keystore.jks");
654
+ if (_fs.default.existsSync(keystorePath)) {
655
+ const finalKeystorePath = _path.default.join(outputDir, `${androidConfig.newProjectName}-keystore.jks`);
656
+ _fs.default.copyFileSync(keystorePath, finalKeystorePath);
657
+ progress.log(`Keystore copied to: ${finalKeystorePath}`, "success");
658
+ }
659
+
660
+ // Clean up temporary deployment directory
661
+ await cleanupTempDeployment(tempDeploymentPath);
662
+ return {
663
+ ...aabResult,
664
+ finalAabPath,
665
+ finalAabName,
666
+ outputDir
667
+ };
668
+ } catch (error) {
669
+ throw new Error(`Failed to finalize AAB: ${error.message}`);
670
+ }
671
+ }
672
+ async function cleanupTempDeployment(tempDeploymentPath) {
673
+ if (!tempDeploymentPath || !_fs.default.existsSync(tempDeploymentPath)) {
674
+ return;
675
+ }
676
+ try {
677
+ progress.log("Cleaning up temporary deployment files...", "info");
678
+ (0, _utils.runCommand)(`rm -rf "${tempDeploymentPath}"`);
679
+ progress.log("Temporary files cleaned up successfully", "success");
680
+ } catch (error) {
681
+ progress.log(`Warning: Could not cleanup temporary directory: ${error.message}`, "warning");
682
+ }
683
+ }
684
+ async function updateFileContents(projectPaths, androidConfig) {
685
+ const {
686
+ newProjectPath
687
+ } = projectPaths;
688
+ const {
689
+ oldProjectName,
690
+ newProjectName,
691
+ packageName
692
+ } = androidConfig;
693
+ progress.log("Updating file contents and names...", "info");
694
+ try {
695
+ // STEP 1: Update packageName in build.gradle.kts and source files
696
+ if (packageName) {
697
+ const OLD_PACKAGE = "io.yourname.androidproject";
698
+ progress.log(`Replacing package name: ${OLD_PACKAGE} → ${packageName}`, "info");
699
+
700
+ // Update build.gradle.kts
701
+ const buildGradlePath = _path.default.join(newProjectPath, "app", "build.gradle.kts");
702
+ if (_fs.default.existsSync(buildGradlePath)) {
703
+ let content = _fs.default.readFileSync(buildGradlePath, "utf8");
704
+ content = content.replace(/namespace = "io\.yourname\.androidproject"/g, `namespace = "${packageName}"`);
705
+ content = content.replace(/applicationId = "io\.yourname\.androidproject"/g, `applicationId = "${packageName}"`);
706
+ _fs.default.writeFileSync(buildGradlePath, content, "utf8");
707
+ progress.log(`✅ Updated build.gradle.kts with package: ${packageName}`, "success");
708
+ }
709
+
710
+ // Update source directory structure for all source sets
711
+ const sourceSets = ["main", "test", "noFcm", "withFcm"];
712
+ const newPackageParts = packageName.split(".");
713
+ let totalUpdatedFiles = 0;
714
+ for (const sourceSet of sourceSets) {
715
+ const oldPackagePath = _path.default.join(newProjectPath, "app", "src", sourceSet, "java", "io", "yourname", "androidproject");
716
+ if (_fs.default.existsSync(oldPackagePath)) {
717
+ const newPackagePath = _path.default.join(newProjectPath, "app", "src", sourceSet, "java", ...newPackageParts);
718
+ const packagePathChanged = _path.default.resolve(oldPackagePath) !== _path.default.resolve(newPackagePath);
719
+ if (packagePathChanged) {
720
+ // Create new package directory
721
+ const newPackageDir = _path.default.dirname(newPackagePath);
722
+ _fs.default.mkdirSync(newPackageDir, {
723
+ recursive: true
724
+ })
725
+
726
+ // Move source files
727
+ ;
728
+ (0, _utils.runCommand)(`mv "${oldPackagePath}" "${newPackagePath}"`);
729
+ progress.log(`✅ Moved ${sourceSet} source files to: ${newPackagePath}`, "success");
730
+ } else {
731
+ progress.log(`Package path unchanged for ${sourceSet}; skipping source directory move`, "info");
732
+ }
733
+
734
+ // Update package declarations in source files
735
+ const findCommand = `find "${newPackagePath}" -type f \\( -name "*.kt" -o -name "*.java" \\)`;
736
+ const sourceFiles = (0, _utils.runCommand)(findCommand).trim().split("\n").filter(f => f.trim());
737
+ let updatedCount = 0;
738
+ for (const file of sourceFiles) {
739
+ let content = _fs.default.readFileSync(file, "utf8");
740
+ if (content.includes(OLD_PACKAGE)) {
741
+ content = content.replace(new RegExp(OLD_PACKAGE, "g"), packageName);
742
+ _fs.default.writeFileSync(file, content, "utf8");
743
+ updatedCount++;
744
+ }
745
+ }
746
+ totalUpdatedFiles += updatedCount;
747
+ progress.log(`✅ Updated package declarations in ${updatedCount} ${sourceSet} files`, "success");
748
+
749
+ // Clean up old empty directories
750
+ try {
751
+ ;
752
+ (0, _utils.runCommand)(`find "${_path.default.join(newProjectPath, "app", "src", sourceSet, "java")}" -type d -empty -delete`);
753
+ } catch (err) {
754
+ // Ignore cleanup errors
755
+ }
756
+ }
757
+ }
758
+ if (totalUpdatedFiles > 0) {
759
+ progress.log(`✅ Total package declarations updated: ${totalUpdatedFiles} files`, "success");
760
+ }
761
+
762
+ // Update AndroidManifest.xml
763
+ const manifestPath = _path.default.join(newProjectPath, "app", "src", "main", "AndroidManifest.xml");
764
+ if (_fs.default.existsSync(manifestPath)) {
765
+ let content = _fs.default.readFileSync(manifestPath, "utf8");
766
+ if (content.includes(OLD_PACKAGE)) {
767
+ content = content.replace(new RegExp(OLD_PACKAGE, "g"), packageName);
768
+ _fs.default.writeFileSync(manifestPath, content, "utf8");
769
+ progress.log(`✅ Updated AndroidManifest.xml with package: ${packageName}`, "success");
770
+ }
771
+ }
772
+
773
+ // Update proguard-rules.pro
774
+ const proguardPath = _path.default.join(newProjectPath, "app", "proguard-rules.pro");
775
+ if (_fs.default.existsSync(proguardPath)) {
776
+ let content = _fs.default.readFileSync(proguardPath, "utf8");
777
+ if (content.includes(OLD_PACKAGE)) {
778
+ content = content.replace(new RegExp(OLD_PACKAGE.replace(/\./g, "\\."), "g"), packageName);
779
+ _fs.default.writeFileSync(proguardPath, content, "utf8");
780
+ progress.log(`✅ Updated proguard-rules.pro with package: ${packageName}`, "success");
781
+ }
782
+ }
783
+ }
784
+
785
+ // STEP 2: Find and rename files that contain the old project name
786
+ const findFilesCommand = `find "${newProjectPath}" -type f -name "*${oldProjectName}*"`;
787
+ const fileResult = (0, _utils.runCommand)(findFilesCommand);
788
+ if (fileResult.trim()) {
789
+ const filesToRename = fileResult.trim().split("\n").filter(file => file.trim());
790
+ for (const file of filesToRename) {
791
+ const newFileName = file.split(oldProjectName).join(newProjectName);
792
+ if (file !== newFileName) {
793
+ ;
794
+ (0, _utils.runCommand)(`mv "${file}" "${newFileName}"`);
795
+ progress.log(`Renamed file: ${_path.default.basename(file)} → ${_path.default.basename(newFileName)}`, "success");
796
+ }
797
+ }
798
+ }
799
+
800
+ // STEP 3: Update file contents that reference the old project name
801
+ const fileTypes = ["*.gradle", "*.gradle.kts", "*.xml", "*.json", "*.properties", "*.java", "*.kt", "*.kts", "*.js", "*.ts", "*.md"];
802
+ for (const fileType of fileTypes) {
803
+ try {
804
+ const findContentCommand = `find "${newProjectPath}" -name "${fileType}" -type f`;
805
+ const files = (0, _utils.runCommand)(findContentCommand);
806
+ if (files.trim()) {
807
+ const fileList = files.trim().split("\n").filter(file => file.trim());
808
+ for (const file of fileList) {
809
+ try {
810
+ const content = _fs.default.readFileSync(file, "utf8");
811
+ if (content.includes(oldProjectName)) {
812
+ const updatedContent = content.split(oldProjectName).join(newProjectName);
813
+ _fs.default.writeFileSync(file, updatedContent, "utf8");
814
+ progress.log(`Updated content in: ${_path.default.relative(newProjectPath, file)}`, "info");
815
+ }
816
+ } catch (fileError) {
817
+ progress.log(`Warning: Could not update file ${file}: ${fileError.message}`, "warning");
818
+ }
819
+ }
820
+ }
821
+ } catch (typeError) {
822
+ // Continue with other file types if one fails
823
+ progress.log(`Warning: Error processing ${fileType} files: ${typeError.message}`, "warning");
824
+ }
825
+ }
826
+ progress.log("File content updates completed", "success");
827
+ } catch (error) {
828
+ throw new Error(`Error updating file contents: ${error.message}`);
829
+ }
830
+ }
831
+ async function buildAndroidAAB(configPathOrConfig) {
832
+ let androidConfig;
833
+ let projectPaths;
834
+ let backupPath;
835
+ let tempDeploymentPath;
836
+ try {
837
+ // Initialize configuration - handle both config path and direct config object
838
+ progress.start("config");
839
+ if (typeof configPathOrConfig === "string") {
840
+ const {
841
+ android
842
+ } = await initializeConfig(configPathOrConfig);
843
+ androidConfig = android;
844
+ } else {
845
+ // Direct config object passed (from buildAppAndroid.js)
846
+ // All defaults should already be applied in buildAppAndroid.js
847
+ androidConfig = configPathOrConfig;
848
+
849
+ // Validate required fields
850
+ if (!androidConfig.newProjectName) {
851
+ throw new Error("newProjectName is required in android config");
852
+ }
853
+ if (!androidConfig.projectPath) {
854
+ throw new Error("projectPath is required in android config");
855
+ }
856
+ if (!androidConfig.oldProjectName) {
857
+ throw new Error("oldProjectName is required in android config");
858
+ }
859
+ progress.log(`Using configuration from buildAppAndroid.js`, "info");
860
+ progress.log(`Project path: ${androidConfig.projectPath}`, "info");
861
+ progress.log(`Old project name: ${androidConfig.oldProjectName}`, "info");
862
+ progress.log(`New project name: ${androidConfig.newProjectName}`, "info");
863
+ progress.log(`Deployment path: ${androidConfig.deploymentPath || "Not specified"}`, "info");
864
+ progress.log(`Overwrite existing: ${androidConfig.overwriteExisting}`, "info");
865
+ }
866
+ progress.complete("config");
867
+
868
+ // Validate project structure
869
+ progress.start("validation");
870
+ projectPaths = await validateProjectStructure(androidConfig);
871
+ progress.complete("validation");
872
+
873
+ // Create backup if requested
874
+ progress.start("backup");
875
+ backupPath = await createBackup(projectPaths, androidConfig);
876
+ progress.complete("backup");
877
+
878
+ // Create temporary deployment project copy
879
+ progress.start("createTempDeployment");
880
+ tempDeploymentPath = await createTempDeploymentProject(projectPaths, androidConfig);
881
+ progress.complete("createTempDeployment");
882
+
883
+ // Update file contents in temp deployment
884
+ progress.start("updateFileContents");
885
+ await updateFileContents({
886
+ ...projectPaths,
887
+ newProjectPath: tempDeploymentPath
888
+ }, androidConfig);
889
+ progress.complete("updateFileContents");
890
+
891
+ // Create keystore if needed
892
+ progress.start("createKeystore");
893
+ const keystorePath = await createKeystore({
894
+ ...projectPaths,
895
+ newProjectPath: tempDeploymentPath
896
+ }, androidConfig);
897
+ progress.complete("createKeystore");
898
+
899
+ // Create signed AAB if keystore was created/found and enabled in config
900
+ let aabResult = null;
901
+ if (keystorePath && androidConfig.createSignedAAB) {
902
+ progress.start("createSignedAAB");
903
+ aabResult = await createSignedAAB({
904
+ ...projectPaths,
905
+ newProjectPath: tempDeploymentPath
906
+ }, androidConfig, keystorePath);
907
+ progress.complete("createSignedAAB");
908
+
909
+ // Copy AAB to final location and cleanup temp deployment
910
+ if (aabResult) {
911
+ progress.start("finalizeAAB");
912
+ aabResult = await finalizeAABAndCleanup(projectPaths, androidConfig, aabResult, tempDeploymentPath);
913
+ progress.complete("finalizeAAB");
914
+ }
915
+ } else if (!androidConfig.createSignedAAB) {
916
+ progress.log("Signed AAB creation skipped (not enabled in config)", "info");
917
+ // DON'T Clean up temp deployment - keep it for verification
918
+ progress.log(`Temp renamed project kept at: ${tempDeploymentPath}`, "info");
919
+ // await cleanupTempDeployment(tempDeploymentPath)
920
+ } else if (!keystorePath) {
921
+ progress.log("Signed AAB creation skipped (no keystore available)", "warning");
922
+ // DON'T Clean up temp deployment - keep it for verification
923
+ progress.log(`Temp renamed project kept at: ${tempDeploymentPath}`, "info");
924
+ // await cleanupTempDeployment(tempDeploymentPath)
925
+ }
926
+
927
+ // Print completion summary
928
+ progress.printTreeContent("Build Summary", ["Android AAB build completed successfully:", {
929
+ text: `Original project: ${projectPaths.oldProjectPath}`,
930
+ indent: 1,
931
+ prefix: "├─ ",
932
+ color: "gray"
933
+ }, {
934
+ text: `Project name: ${androidConfig.oldProjectName} → ${androidConfig.newProjectName}`,
935
+ indent: 1,
936
+ prefix: "├─ ",
937
+ color: "gray"
938
+ }, {
939
+ text: `Deployment path: ${androidConfig.deploymentPath || "Not specified"}`,
940
+ indent: 1,
941
+ prefix: "├─ ",
942
+ color: "gray"
943
+ }, {
944
+ text: `Overwrite existing: ${androidConfig.overwriteExisting}`,
945
+ indent: 1,
946
+ prefix: "├─ ",
947
+ color: "gray"
948
+ }, {
949
+ text: `Backup created: ${backupPath ? "Yes" : "No"}`,
950
+ indent: 1,
951
+ prefix: "├─ ",
952
+ color: "gray"
953
+ }, {
954
+ text: `Keystore: ${keystorePath ? "Created/Verified" : "Skipped"}`,
955
+ indent: 1,
956
+ prefix: "├─ ",
957
+ color: "gray"
958
+ }, {
959
+ text: `Signed AAB: ${aabResult ? "Created" : "Skipped"}`,
960
+ indent: 1,
961
+ prefix: "└─ ",
962
+ color: "gray"
963
+ }, "", "Output Files:", {
964
+ text: `Original preserved at: ${projectPaths.oldProjectPath}`,
965
+ indent: 1,
966
+ prefix: "├─ ",
967
+ color: "green"
968
+ }, ...(backupPath ? [{
969
+ text: `Backup: ${backupPath}`,
970
+ indent: 1,
971
+ prefix: "├─ ",
972
+ color: "gray"
973
+ }] : []), ...(aabResult ? [{
974
+ text: `AAB file: ${aabResult.finalAabPath}`,
975
+ indent: 1,
976
+ prefix: "├─ ",
977
+ color: "green"
978
+ }, {
979
+ text: `AAB size: ${aabResult.fileSize} MB`,
980
+ indent: 1,
981
+ prefix: "└─ ",
982
+ color: "green"
983
+ }] : [{
984
+ text: `No AAB created`,
985
+ indent: 1,
986
+ prefix: "└─ ",
987
+ color: "gray"
988
+ }])]);
989
+ process.exit(0);
990
+ } catch (error) {
991
+ // Clean up temp deployment on error
992
+ if (tempDeploymentPath) {
993
+ await cleanupTempDeployment(tempDeploymentPath);
994
+ }
995
+ if (progress.currentStep) {
996
+ progress.fail(progress.currentStep.id, error.message);
997
+ progress.printTreeContent("Troubleshooting Guide", ["Build operation failed. Please check the following:", {
998
+ text: "Verify config file exists and contains required android configuration",
999
+ indent: 1,
1000
+ prefix: "├─ ",
1001
+ color: "yellow"
1002
+ }, {
1003
+ text: "Ensure project path exists and is accessible",
1004
+ indent: 1,
1005
+ prefix: "├─ ",
1006
+ color: "yellow"
1007
+ }, {
1008
+ text: "Check file/directory permissions",
1009
+ indent: 1,
1010
+ prefix: "├─ ",
1011
+ color: "yellow"
1012
+ }, {
1013
+ text: "Verify no processes are using the project directory",
1014
+ indent: 1,
1015
+ prefix: "├─ ",
1016
+ color: "yellow"
1017
+ }, {
1018
+ text: "Ensure sufficient disk space for project copy",
1019
+ indent: 1,
1020
+ prefix: "├─ ",
1021
+ color: "yellow"
1022
+ }, {
1023
+ text: 'Update keystore passwords (do not use "your_store_password")',
1024
+ indent: 1,
1025
+ prefix: "└─ ",
1026
+ color: "yellow"
1027
+ }, "\nConfiguration Details:", {
1028
+ text: `Config: ${typeof configPathOrConfig === "string" ? configPathOrConfig : "Direct object from buildAppAndroid.js"}`,
1029
+ indent: 1,
1030
+ prefix: "├─ ",
1031
+ color: "gray"
1032
+ }, {
1033
+ text: `Project path: ${androidConfig?.projectPath || "Not loaded"}`,
1034
+ indent: 1,
1035
+ prefix: "├─ ",
1036
+ color: "gray"
1037
+ }, {
1038
+ text: `Deployment path: ${androidConfig?.deploymentPath || "Not loaded"}`,
1039
+ indent: 1,
1040
+ prefix: "├─ ",
1041
+ color: "gray"
1042
+ }, {
1043
+ text: `Old project name: ${androidConfig?.oldProjectName || "Not loaded"}`,
1044
+ indent: 1,
1045
+ prefix: "├─ ",
1046
+ color: "gray"
1047
+ }, {
1048
+ text: `New project name: ${androidConfig?.newProjectName || "Not loaded"}`,
1049
+ indent: 1,
1050
+ prefix: "├─ ",
1051
+ color: "gray"
1052
+ }, {
1053
+ text: `Overwrite existing: ${androidConfig?.overwriteExisting !== undefined ? androidConfig.overwriteExisting : "Not loaded"}`,
1054
+ indent: 1,
1055
+ prefix: "└─ ",
1056
+ color: "gray"
1057
+ }, "\nFor keystore issues, ensure your config has:", {
1058
+ text: "Real passwords (not placeholder values)",
1059
+ indent: 1,
1060
+ prefix: "├─ ",
1061
+ color: "cyan"
1062
+ }, {
1063
+ text: "Valid organization information",
1064
+ indent: 1,
1065
+ prefix: "├─ ",
1066
+ color: "cyan"
1067
+ }, {
1068
+ text: "Consistent store and key passwords for PKCS12",
1069
+ indent: 1,
1070
+ prefix: "└─ ",
1071
+ color: "cyan"
1072
+ }]);
1073
+ }
1074
+ process.exit(1);
1075
+ }
1076
+ }
1077
+
1078
+ // Export functions for use as a module - using the same export pattern as buildAppAndroid.js
87
1079
  // This allows the functions to be imported with ES6 import syntax from other modules
88
- exports.buildAndroidAAB=buildAndroidAAB;exports.createAndroidDeployment=buildAndroidAAB;exports.renameAndroidProject=buildAndroidAAB;// Execute if run directly
89
- if(require.main===module){const configPath=process.argv[2];if(!configPath){console.error("Usage: node renameAndroidProject.js <config-path>");process.exit(1);}buildAndroidAAB(configPath).catch(error=>{console.error("Script failed:",error.message);process.exit(1);});}
1080
+ exports.buildAndroidAAB = buildAndroidAAB;
1081
+ exports.createAndroidDeployment = buildAndroidAAB;
1082
+ exports.renameAndroidProject = buildAndroidAAB;
1083
+
1084
+ // Execute if run directly
1085
+ if (require.main === module) {
1086
+ const configPath = process.argv[2];
1087
+ if (!configPath) {
1088
+ console.error("Usage: node renameAndroidProject.js <config-path>");
1089
+ process.exit(1);
1090
+ }
1091
+ buildAndroidAAB(configPath).catch(error => {
1092
+ console.error("Script failed:", error.message);
1093
+ process.exit(1);
1094
+ });
1095
+ }