From 51fb6f5296a03bd489d81702fe35ce2d1d494fd1 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 1 Jul 2026 15:49:09 +0200 Subject: [PATCH 1/5] chore(cli): migrate changes for Capacitor 9 --- cli/src/tasks/migrate.ts | 90 ++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 0bc891e49..fa0a76056 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -1,6 +1,6 @@ import { writeFileSync, readFileSync, existsSync } from 'fs-extra'; import { join } from 'path'; -import rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; import { coerce, gte, lt } from 'semver'; import c from '../colors'; @@ -43,8 +43,8 @@ const plugins = [ '@capacitor/text-zoom', '@capacitor/toast', ]; -const coreVersion = '^8.0.0'; -const pluginVersion = '^8.0.0'; +const coreVersion = 'next'; +const pluginVersion = 'next'; const gradleVersion = '9.5.1'; const iOSVersion = '16'; const kotlinVersion = '2.4.0'; @@ -56,14 +56,14 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem } const capMajor = await checkCapacitorMajorVersion(config); - if (capMajor < 7) { - fatal('Migrate can only be used on Capacitor 7, please use the CLI in Capacitor 7 to upgrade to 7 first'); + if (capMajor < 8) { + fatal('Migrate can only be used on Capacitor 8, please use the CLI in Capacitor 8 to upgrade to 8 first'); } const jdkMajor = await checkJDKMajorVersion(); if (jdkMajor < 21) { - logger.warn('Capacitor 8 requires JDK 21 or higher. Some steps may fail.'); + logger.warn('Capacitor 9 requires JDK 21 or higher. Some steps may fail.'); } const variablesAndClasspaths: @@ -84,13 +84,13 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem }; const monorepoWarning = - 'Please note this tool is not intended for use in a mono-repo environment, you should migrate manually instead. Refer to https://capacitorjs.com/docs/next/updating/8-0'; + 'Please note this tool is not intended for use in a mono-repo environment, you should migrate manually instead. Refer to https://capacitorjs.com/docs/next/updating/9-0'; logger.info(monorepoWarning); const { migrateconfirm } = noprompt ? { migrateconfirm: 'y' } - : await logPrompt(`Capacitor 8 sets a deployment target of iOS ${iOSVersion} and Android 17 (SDK 37). \n`, { + : await logPrompt(`Capacitor 9 sets a deployment target of iOS ${iOSVersion} and Android 17 (SDK 37). \n`, { type: 'text', name: 'migrateconfirm', message: `Are you sure you want to migrate? (Y/n)`, @@ -175,6 +175,12 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem } else { logger.warn('Skipped updating deployment target'); } + + await runTask(`Migrating AppDelegate.swift`, () => { + return updateAppDelegate( + join(config.ios.nativeTargetDirAbs, 'AppDelegate.swift') + ); + }); } if (!installFailed) { @@ -228,6 +234,20 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem return updateAppBuildGradle(join(config.android.appDirAbs, 'build.gradle')); }); + // settings.gradle + await runTask(`Migrating settings.gradle file.`, () => { + return (async (): Promise => { + const settingsPath = join(config.android.platformDirAbs, 'settings.gradle'); + let txt = readFile(settingsPath); + if (!txt) { + return; + } + txt = txt.replace(`include ':capacitor-cordova-android-plugins'\n`, ''); + txt = txt.replace(`project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')\n`, ''); + writeFileSync(settingsPath, txt, { encoding: 'utf-8' }); + })(); + }); + // Variables gradle await runTask(`Migrating variables.gradle file.`, () => { return (async (): Promise => { @@ -336,14 +356,14 @@ async function installLatestLibs(dependencyManager: string, runInstall: boolean, for (const devDepKey of Object.keys(pkgJson['devDependencies'] || {})) { if (libs.includes(devDepKey)) { pkgJson['devDependencies'][devDepKey] = coreVersion; - } else if (plugins.includes(devDepKey)) { + } else if (plugins.includes(devDepKey) && !pkgJson['devDependencies'][devDepKey].startsWith('file:')) { pkgJson['devDependencies'][devDepKey] = pluginVersion; } } for (const depKey of Object.keys(pkgJson['dependencies'] || {})) { if (libs.includes(depKey)) { pkgJson['dependencies'][depKey] = coreVersion; - } else if (plugins.includes(depKey)) { + } else if (plugins.includes(depKey) && !pkgJson['dependencies'][depKey].startsWith('file:')) { pkgJson['dependencies'][depKey] = pluginVersion; } } @@ -367,16 +387,8 @@ async function installLatestLibs(dependencyManager: string, runInstall: boolean, async function writeBreakingChanges() { const breaking = [ - '@capacitor/action-sheet', - '@capacitor/barcode-scanner', - '@capacitor/browser', - '@capacitor/camera', - '@capacitor/geolocation', - '@capacitor/google-maps', '@capacitor/push-notifications', - '@capacitor/screen-orientation', '@capacitor/splash-screen', - '@capacitor/status-bar', ]; const broken = []; for (const lib of breaking) { @@ -386,7 +398,7 @@ async function writeBreakingChanges() { } if (broken.length > 0) { logger.info( - `IMPORTANT: Review https://capacitorjs.com/docs/next/updating/8-0#plugins for breaking changes in these plugins that you use: ${broken.join( + `IMPORTANT: Review https://capacitorjs.com/docs/next/updating/9-0#plugins for breaking changes in these plugins that you use: ${broken.join( ', ', )}.`, ); @@ -518,14 +530,16 @@ async function updateAppBuildGradle(filename: string) { } let replaced = txt; - const gradlePproperties = ['compileSdk', 'namespace', 'ignoreAssetsPattern']; - for (const prop of gradlePproperties) { - // Use updated Groovy DSL syntax with " = " assignment - const regex = new RegExp(`(^\\s*${prop})\\s+(?!=)(.+)$`, 'gm'); - replaced = replaced.replace(regex, (_match, key, value) => { - return `${key} = ${value.trim()}`; - }); - } + replaced = replaced.replace(`proguard-android.txt`, `proguard-android-optimize.txt`); + replaced = replaced.replace(' targetSdkVersion rootProject.ext.targetSdkVersion\n', ''); + replaced = replaced.replace(` implementation project(':capacitor-cordova-android-plugins')\n`, ''); + replaced = replaced.replace(`repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +`, ""); writeFileSync(filename, replaced, 'utf-8'); } @@ -622,3 +636,25 @@ async function updateAndroidManifest(filename: string) { writeFileSync(filename, replaced, 'utf-8'); } } + +async function updateAppDelegate(filename: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + + if (txt.includes('@main')) { + return; // Probably already updated + } + // Since navigation was an optional change in Capacitor 7, attempting to add density and/or navigation + const replaced = txt + .replace( + '@UIApplicationMain', + '@main', + ) + if (!replaced.includes('@main')) { + logger.error(`Unable to replace @UIApplicationMain to @main in ${filename}. Try replacing it manually`); + } else { + writeFileSync(filename, replaced, 'utf-8'); + } +} From e1f34256356bc3f8a7ba478015b727bf98486e39 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 1 Jul 2026 16:04:45 +0200 Subject: [PATCH 2/5] fmt --- cli/src/tasks/migrate.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index fa0a76056..51a8349e7 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -177,9 +177,7 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem } await runTask(`Migrating AppDelegate.swift`, () => { - return updateAppDelegate( - join(config.ios.nativeTargetDirAbs, 'AppDelegate.swift') - ); + return updateAppDelegate(join(config.ios.nativeTargetDirAbs, 'AppDelegate.swift')); }); } @@ -243,7 +241,10 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem return; } txt = txt.replace(`include ':capacitor-cordova-android-plugins'\n`, ''); - txt = txt.replace(`project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')\n`, ''); + txt = txt.replace( + `project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')\n`, + '', + ); writeFileSync(settingsPath, txt, { encoding: 'utf-8' }); })(); }); @@ -386,10 +387,7 @@ async function installLatestLibs(dependencyManager: string, runInstall: boolean, } async function writeBreakingChanges() { - const breaking = [ - '@capacitor/push-notifications', - '@capacitor/splash-screen', - ]; + const breaking = ['@capacitor/push-notifications', '@capacitor/splash-screen']; const broken = []; for (const lib of breaking) { if (allDependencies[lib]) { @@ -533,13 +531,16 @@ async function updateAppBuildGradle(filename: string) { replaced = replaced.replace(`proguard-android.txt`, `proguard-android-optimize.txt`); replaced = replaced.replace(' targetSdkVersion rootProject.ext.targetSdkVersion\n', ''); replaced = replaced.replace(` implementation project(':capacitor-cordova-android-plugins')\n`, ''); - replaced = replaced.replace(`repositories { + replaced = replaced.replace( + `repositories { flatDir{ dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' } } -`, ""); +`, + '', + ); writeFileSync(filename, replaced, 'utf-8'); } @@ -647,11 +648,7 @@ async function updateAppDelegate(filename: string) { return; // Probably already updated } // Since navigation was an optional change in Capacitor 7, attempting to add density and/or navigation - const replaced = txt - .replace( - '@UIApplicationMain', - '@main', - ) + const replaced = txt.replace('@UIApplicationMain', '@main'); if (!replaced.includes('@main')) { logger.error(`Unable to replace @UIApplicationMain to @main in ${filename}. Try replacing it manually`); } else { From c90405c74c94082519cafa2502138c7363ff31f6 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 7 Jul 2026 11:01:49 +0200 Subject: [PATCH 3/5] remove comment --- cli/src/tasks/migrate.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 51a8349e7..8c301a07f 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -647,7 +647,6 @@ async function updateAppDelegate(filename: string) { if (txt.includes('@main')) { return; // Probably already updated } - // Since navigation was an optional change in Capacitor 7, attempting to add density and/or navigation const replaced = txt.replace('@UIApplicationMain', '@main'); if (!replaced.includes('@main')) { logger.error(`Unable to replace @UIApplicationMain to @main in ${filename}. Try replacing it manually`); From 310bee0c6e95ee7f63fba9d35afc77aedf57f2a2 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 8 Jul 2026 17:23:59 +0200 Subject: [PATCH 4/5] don't update workspace dependency --- cli/src/tasks/migrate.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 8c301a07f..81691b633 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -357,14 +357,23 @@ async function installLatestLibs(dependencyManager: string, runInstall: boolean, for (const devDepKey of Object.keys(pkgJson['devDependencies'] || {})) { if (libs.includes(devDepKey)) { pkgJson['devDependencies'][devDepKey] = coreVersion; - } else if (plugins.includes(devDepKey) && !pkgJson['devDependencies'][devDepKey].startsWith('file:')) { + } else if ( + plugins.includes(devDepKey) && + !( + pkgJson['devDependencies'][devDepKey].startsWith('file:') || + pkgJson['devDependencies'][devDepKey].startsWith('workspace:') + ) + ) { pkgJson['devDependencies'][devDepKey] = pluginVersion; } } for (const depKey of Object.keys(pkgJson['dependencies'] || {})) { if (libs.includes(depKey)) { pkgJson['dependencies'][depKey] = coreVersion; - } else if (plugins.includes(depKey) && !pkgJson['dependencies'][depKey].startsWith('file:')) { + } else if ( + plugins.includes(depKey) && + !(pkgJson['dependencies'][depKey].startsWith('file:') || pkgJson['dependencies'][depKey].startsWith('workspace:')) + ) { pkgJson['dependencies'][depKey] = pluginVersion; } } From 882344c6f81d907cfff9faaf4fa09f0ce5ffe0cb Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 8 Jul 2026 17:30:48 +0200 Subject: [PATCH 5/5] remove update kotlin version --- cli/src/tasks/migrate.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 81691b633..fbe398b76 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -47,7 +47,6 @@ const coreVersion = 'next'; const pluginVersion = 'next'; const gradleVersion = '9.5.1'; const iOSVersion = '16'; -const kotlinVersion = '2.4.0'; let installFailed = false; export async function migrateCommand(config: Config, noprompt: boolean, packagemanager: string): Promise { @@ -511,25 +510,9 @@ async function updateBuildGradle( } } - const beforeKotlinVersionUpdate = replaced; - replaced = replaceVersion(replaced, /(ext\.kotlin_version\s*=\s*['"])([^'"]+)(['"])/, kotlinVersion); - replaced = replaceVersion(replaced, /(org\.jetbrains\.kotlin:kotlin[^:]*:)([\d.]+)(['"])/, kotlinVersion); - if (beforeKotlinVersionUpdate !== replaced) { - logger.info(`Set Kotlin version to ${kotlinVersion}`); - } writeFileSync(filename, replaced, 'utf-8'); } -function replaceVersion(text: string, regex: RegExp, newVersion: string): string { - return text.replace(regex, (match, prefix, currentVersion, suffix) => { - const semVer = coerce(currentVersion)?.version; - if (gte(newVersion, semVer ? semVer : '0.0.0')) { - return `${prefix || ''}${newVersion}${suffix || ''}`; - } - return match; - }); -} - async function updateAppBuildGradle(filename: string) { const txt = readFile(filename); if (!txt) {