Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
248a4b7
feat: migrate from cap 7 to cap 8
ItsChaceD Nov 6, 2025
0a61107
add kotlin 2.2 migration
ItsChaceD Nov 10, 2025
de0954e
fix propName deprecations and simplify kotlinOptions replacement
ItsChaceD Nov 11, 2025
33fadb4
change plugin version to 8.0.0
ItsChaceD Nov 11, 2025
d83c2ea
update lib versions, package version, and replace compileSdkVersion
ItsChaceD Nov 12, 2025
4334174
update core version to next
ItsChaceD Nov 12, 2025
ddb2804
remove compileSdkVersion logic
ItsChaceD Nov 12, 2025
a5e9677
update android lib versions
ItsChaceD Nov 12, 2025
a4d9370
revert core version to alpha
ItsChaceD Nov 12, 2025
ee5c903
add carat back in
ItsChaceD Nov 13, 2025
402c007
optionally set kotlin version
ItsChaceD Nov 13, 2025
7895323
change kotlin start
ItsChaceD Nov 14, 2025
cddf24f
update npm dependencies
ItsChaceD Nov 14, 2025
c0ea9d0
update to beta
ItsChaceD Nov 14, 2025
8505134
fix kotlin version regex and update npm dependencies in migrate script
ItsChaceD Nov 19, 2025
9bbd468
change to 8.0.0 and cleanup prettier logic
ItsChaceD Nov 20, 2025
592e401
update package.json version
OS-ruimoreiramendes Nov 27, 2025
c71a2d5
added missing variables
OS-ruimoreiramendes Nov 27, 2025
79d2753
update missing variables
OS-ruimoreiramendes Nov 27, 2025
b6659fa
remove unnecessary properties
OS-ruimoreiramendes Nov 27, 2025
2ab8393
fix updateKotlinOptions to remove whitespace and update JVM version
OS-ruimoreiramendes Nov 28, 2025
5ae21b7
update logger.info message in updateKotlinOptions
OS-ruimoreiramendes Dec 2, 2025
187033b
added new regex to update kotlin_version
OS-ruimoreiramendes Dec 2, 2025
c973568
added new regex to update kotlin_version
OS-ruimoreiramendes Dec 2, 2025
8794872
update typescript and types/node
OS-ruimoreiramendes Dec 2, 2025
2aa42c6
change typeScript and types/node update logic
OS-ruimoreiramendes Dec 2, 2025
e60e5fb
remove types/node check
OS-ruimoreiramendes Dec 2, 2025
1b6e88f
update @capacitor/docgen version
OS-ruimoreiramendes Dec 3, 2025
fd9e010
fix kotlin-stdlib transform and add new tranform for kotlin-gradle-pl…
OS-ruimoreiramendes Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Migrate Capacitor Plugin

Migrate a Capacitor 6 plugin to Capacitor 7.
Migrate a Capacitor 7 plugin to Capacitor 8.

## Usage

```
npx @capacitor/plugin-migration-v6-to-v7@latest
npx @capacitor/plugin-migration-v7-to-v8@latest
```
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@capacitor/plugin-migration-v6-to-v7",
"version": "0.0.7",
"description": "Utility to help migrate Capacitor 6 plugins to Capacitor 7",
"name": "@capacitor/plugin-migration-v7-to-v8",
"version": "1.0.0-beta.1",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"version": "1.0.0-beta.1",
"version": "0.0.1",

The package version should be reset to 0.0.1, this is an experimental package and whenever we change the name for the next major version the version should be 0.0.1, we can do breaking changes if needed since it hasn't reached version 1.0.0

"description": "Utility to help migrate Capacitor 7 plugins to Capacitor 8",
"main": "./dist/index.js",
"scripts": {
"lint": "npm run eslint && npm run prettier -- --check",
Expand All @@ -18,7 +18,7 @@
"url": "git+https://github.com/ionic-team/migrate-capacitor-plugin.git"
},
"bin": {
"plugin-migration-v6-to-v7": "bin/migrate-capacitor-plugin"
"plugin-migration-v7-to-v8": "bin/migrate-capacitor-plugin"
},
"files": [
"bin/",
Expand All @@ -41,17 +41,17 @@
"@ionic/eslint-config": "^0.4.0",
"@ionic/prettier-config": "^4.0.0",
"@types/fs-extra": "^11.0.4",
"@types/node": "^20.17.6",
"@types/node": "^24.10.1",
"eslint": "^8.57.0",
"prettier": "^3.3.3",
"typescript": "^5.0.4"
"prettier": "^3.6.2",
"typescript": "^5.9.3"
},
"dependencies": {
"@ionic/cli-framework-output": "^2.2.8",
"@ionic/utils-subprocess": "^3.0.1",
"fs-extra": "^11.2.0",
"fs-extra": "^11.3.2",
"kleur": "^4.1.5",
"rimraf": "^6.0.1",
"semver": "^7.6.3"
"rimraf": "^6.1.0",
"semver": "^7.7.3"
}
}
}
176 changes: 136 additions & 40 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,40 @@ import { rimraf } from 'rimraf';
import { logger } from './log';
import { runCommand } from './subprocess';

const coreVersion = '7.0.0';
const gradleVersion = '8.11.1';
const AGPVersion = '8.7.2';
const gmsVersion = '4.4.2';
const kotlinVersion = '1.9.25';
const coreVersion = '8.0.0-beta.0';
const gradleVersion = '8.14.3';
const AGPVersion = '8.13.0';
const gmsVersion = '4.4.4';
const kotlinVersion = '2.2.20';
const docgenVersion = '^0.3.0';
const eslintVersion = '^8.57.0';
const eslintVersion = '^8.57.1';
const ionicEslintVersion = '^0.4.0';
const ionicPrettierVersion = '^4.0.0';
const ionicSwiftlintVersion = '^2.0.0';
const prettierJavaVersion = '^2.6.6';
const prettierVersion = '^3.4.2';
const rimrafVersion = '^6.0.1';
const rollupVersion = '^4.30.1';
let updatePrettierJava = false;
const prettierJavaVersion = '^2.7.7';
const prettierVersion = '^3.6.2';
const rimrafVersion = '^6.1.0';
const rollupVersion = '^4.53.2';
let updatePrettierJava = true;
const variables = {
minSdkVersion: 23,
compileSdkVersion: 35,
targetSdkVersion: 35,
androidxActivityVersion: '1.9.2',
androidxAppCompatVersion: '1.7.0',
androidxCoordinatorLayoutVersion: '1.2.0',
androidxCoreVersion: '1.15.0',
androidxFragmentVersion: '1.8.4',
firebaseMessagingVersion: '24.1.0',
minSdkVersion: 24,
compileSdkVersion: 36,
targetSdkVersion: 36,
androidxActivityVersion: '1.11.0',
androidxAppCompatVersion: '1.7.1',
androidxCoordinatorLayoutVersion: '1.3.0',
androidxCoreVersion: '1.17.0',
androidxFragmentVersion: '1.8.9',
firebaseMessagingVersion: '25.0.1',
playServicesLocationVersion: '21.3.0',
androidxBrowserVersion: '1.8.0',
androidxMaterialVersion: '1.12.0',
androidxExifInterfaceVersion: '1.3.7',
coreSplashScreenVersion: '1.0.1',
androidxWebkitVersion: '1.12.1',
androidxBrowserVersion: '1.9.0',
androidxMaterialVersion: '1.13.0',
androidxExifInterfaceVersion: '1.4.1',
coreSplashScreenVersion: '1.2.0',
androidxWebkitVersion: '1.14.0',
junitVersion: '4.13.2',
androidxJunitVersion: '1.2.1',
androidxEspressoCoreVersion: '3.6.1',
androidxJunitVersion: '1.3.0',
androidxEspressoCoreVersion: '3.7.0',
};

process.on('unhandledRejection', (error) => {
Expand Down Expand Up @@ -105,8 +105,8 @@ export const run = async (): Promise<void> => {
pluginJSON.devDependencies['@capacitor/docgen'] = docgenVersion;
}

if (pluginJSON.version.startsWith('6.')) {
pluginJSON.version = '7.0.0';
if (pluginJSON.version?.startsWith('7.')) {
pluginJSON.version = '8.0.0';
}

await writeJSON(packageJson, pluginJSON, { spaces: 2 });
Expand Down Expand Up @@ -196,10 +196,10 @@ export const run = async (): Promise<void> => {
join(iosDir, 'Plugin.xcodeproj', 'project.pbxproj'),
'IPHONEOS_DEPLOYMENT_TARGET = ',
';',
'14.0',
'15.0',
);
await updateFile(join(iosDir, 'Podfile'), `platform :ios, '`, `'`, '14.0');
await updateFile(join(dir, 'Package.swift'), '[.iOS(.v', ')],', '14');
await updateFile(join(iosDir, 'Podfile'), `platform :ios, '`, `'`, '15.0');
await updateFile(join(dir, 'Package.swift'), '[.iOS(.v', ')],', '15');
await updateFile(
join(dir, 'Package.swift'),
'.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git",',
Expand All @@ -210,7 +210,11 @@ export const run = async (): Promise<void> => {
}
}

logger.info('Plugin migrated to Capacitor 7!');
logger.info('Plugin migrated to Capacitor 8!');

logger.info('');
logger.info('⚠️ Note: Prettier has been updated from v2 to v3.');
logger.info('We recommend running your formatting and linting scripts to ensure your codebase is formatted correctly.');
};

function updatePodspec(dir: string, pluginJSON: any) {
Expand All @@ -220,7 +224,7 @@ function updatePodspec(dir: string, pluginJSON: any) {
return false;
}
txt = txt.replace('s.ios.deployment_target =', 's.ios.deployment_target =');
txt = txt.replace(`s.ios.deployment_target = '13.0'`, `s.ios.deployment_target = '14.0'`);
txt = txt.replace(`s.ios.deployment_target = '14.0'`, `s.ios.deployment_target = '15.0'`);
writeFileSync(podspecFile, txt, { encoding: 'utf-8' });
}

Expand Down Expand Up @@ -280,12 +284,12 @@ async function updateBuildGradle(
}
}
}
gradleFile = setAllStringIn(
gradleFile,
`ext.kotlin_version = `,
`\n`,
`project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '${kotlinVersion}'`,
);

const kotlinRegex = /ext\.kotlin_version\s*=\s*(?:project\.hasProperty\("kotlin_version"\)\s*\?\s*rootProject\.ext\.kotlin_version\s*:\s*)?['"]([^'"]+)['"]/;
gradleFile = gradleFile.replace(kotlinRegex, () => {
logger.info(`Set kotlin_version = ${kotlinVersion}.`);
return `ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '${kotlinVersion}'`;
});

gradleFile = setAllStringIn(
gradleFile,
Expand All @@ -294,9 +298,101 @@ async function updateBuildGradle(
`:$kotlin_version`,
);

gradleFile = updateDeprecatedPropertySyntax(gradleFile);
gradleFile = updateKotlinOptions(gradleFile);

writeFileSync(filename, gradleFile, 'utf-8');
}

function updateDeprecatedPropertySyntax(gradleFile: string): string {
const propertiesToUpdate = [
'namespace',
'compileSdk',
'testInstrumentationRunner',
'versionName',
'versionCode',
'url',
'abortOnError',
'warningsAsErrors',
'lintConfig',
'minifyEnabled',
'debugSymbolLevel',
'path',
'version',
'baseline',
'sourceCompatibility',
'targetCompatibility',
];

let result = gradleFile;

for (const prop of propertiesToUpdate) {
const regex = new RegExp(`\\b(${prop})[ \\t]+([^=\\t{:])`, 'g');
result = result.replace(regex, '$1 = $2');
}

return result;
}

function updateKotlinOptions(gradleFile: string): string {
const kotlinOptionsRegex = /kotlinOptions\s*\{\s*jvmTarget\s*=\s*['"](\d+\.?\d*)['"][\s\S]*?\}/;
const match = kotlinOptionsRegex.exec(gradleFile);

if (!match) {
return gradleFile;
}

const jvmTargetValue = match[1];
const enumValue = `JVM_${jvmTargetValue.replace('.', '_')}`;

let result = gradleFile;

if (!result.includes('import org.jetbrains.kotlin.gradle.dsl.JvmTarget')) {
const firstNonCommentLine = result.split('\n').findIndex((line) => {
if (!line) return false;
const trimmed = line.trim();
return trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('/*');
});
const lines = result.split('\n');
if (firstNonCommentLine >= 0) {
lines.splice(firstNonCommentLine, 0, 'import org.jetbrains.kotlin.gradle.dsl.JvmTarget');
result = lines.join('\n');
}
}

result = result.replace(kotlinOptionsRegex, '');
result = result.replace(/\n\s*\n\s*\n/g, '\n\n');

const kotlinBlockRegex = /\nkotlin\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/;
const kotlinBlockMatch = kotlinBlockRegex.exec(result);

const compilerOptionsBlock = ` compilerOptions {\n jvmTarget = JvmTarget.${enumValue}\n }`;

if (kotlinBlockMatch) {
// Kotlin block exists, add compilerOptions to it if not already present
if (!kotlinBlockMatch[0].includes('compilerOptions')) {
const kotlinBlockContent = kotlinBlockMatch[1];
const existingContent = kotlinBlockContent.trimEnd();
const newKotlinBlock = `\nkotlin {${existingContent}\n${compilerOptionsBlock}\n}`;
result = result.replace(kotlinBlockRegex, newKotlinBlock);
}
} else {
// No kotlin block exists, create one after the android block
const androidBlockRegex = /android\s*\{[\s\S]*?\n\}/;
const androidMatch = androidBlockRegex.exec(result);

if (androidMatch) {
const insertPosition = androidMatch.index + androidMatch[0].length;
const kotlinBlock = `\n\nkotlin {\n${compilerOptionsBlock}\n}`;
result = result.slice(0, insertPosition) + kotlinBlock + result.slice(insertPosition);
}
}

logger.info('Updated kotlinOptions to compilerOptions for Kotlin 2.2.0');

return result;
}

function readFile(filename: string): string | undefined {
try {
if (!existsSync(filename)) {
Expand Down