@capacitor/plugin-migration-v6-to-v7
Version:
Utility to help migrate Capacitor 6 plugins to Capacitor 7
299 lines (298 loc) • 13.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const rimraf_1 = require("rimraf");
const log_1 = require("./log");
const subprocess_1 = require("./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 docgenVersion = '^0.3.0';
const eslintVersion = '^8.57.0';
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 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',
playServicesLocationVersion: '21.3.0',
androidxBrowserVersion: '1.8.0',
androidxMaterialVersion: '1.12.0',
androidxExifInterfaceVersion: '1.3.7',
coreSplashScreenVersion: '1.0.1',
androidxWebkitVersion: '1.12.1',
junitVersion: '4.13.2',
androidxJunitVersion: '1.2.1',
androidxEspressoCoreVersion: '3.6.1',
};
process.on('unhandledRejection', (error) => {
process.stderr.write(`ERR: ${error}\n`);
process.exit(1);
});
const run = async () => {
const dir = process.cwd();
const opts = { cwd: dir, stdio: 'inherit' };
const packageJson = (0, path_1.join)(dir, 'package.json');
const prettierIgnore = (0, path_1.join)(dir, '.prettierignore');
const gitIgnore = (0, path_1.join)(dir, '.gitignore');
const pluginJSON = await (0, fs_extra_1.readJSON)(packageJson);
for (const dep of ['@capacitor/ios', '@capacitor/android', '@capacitor/core', '@capacitor/cli']) {
if (pluginJSON.devDependencies?.[dep]) {
pluginJSON.devDependencies[dep] = `^${coreVersion}`;
}
if (pluginJSON.dependencies?.[dep]) {
pluginJSON.dependencies[dep] = `^${coreVersion}`;
}
if (pluginJSON.peerDependencies?.[dep]) {
pluginJSON.peerDependencies[dep] = `>=${coreVersion}`;
}
}
if (pluginJSON.devDependencies?.['@ionic/eslint-config']) {
pluginJSON.devDependencies['@ionic/eslint-config'] = ionicEslintVersion;
if (pluginJSON.devDependencies?.['eslint']) {
pluginJSON.devDependencies['eslint'] = eslintVersion;
}
}
if (pluginJSON.devDependencies?.['@ionic/swiftlint-config']) {
pluginJSON.devDependencies['@ionic/swiftlint-config'] = ionicSwiftlintVersion;
}
if (pluginJSON.devDependencies?.['swiftlint']) {
pluginJSON.devDependencies['swiftlint'] = ionicSwiftlintVersion;
}
if (pluginJSON.devDependencies?.['rimraf']) {
pluginJSON.devDependencies['rimraf'] = rimrafVersion;
}
if (pluginJSON.devDependencies?.['rollup']) {
pluginJSON.devDependencies['rollup'] = rollupVersion;
}
if (pluginJSON.devDependencies?.['@ionic/prettier-config']) {
pluginJSON.devDependencies['@ionic/prettier-config'] = ionicPrettierVersion;
pluginJSON.devDependencies['prettier'] = prettierVersion;
pluginJSON.devDependencies['prettier-plugin-java'] = prettierJavaVersion;
updatePrettierJava = true;
}
if (pluginJSON.devDependencies?.['@capacitor/docgen']) {
pluginJSON.devDependencies['@capacitor/docgen'] = docgenVersion;
}
if (pluginJSON.version.startsWith('6.')) {
pluginJSON.version = '7.0.0';
}
await (0, fs_extra_1.writeJSON)(packageJson, pluginJSON, { spaces: 2 });
let packageJsonText = (0, fs_extra_1.readFileSync)(packageJson, 'utf-8');
if (packageJsonText.includes('rollup.config.js')) {
packageJsonText = packageJsonText.replace('rollup.config.js', 'rollup.config.mjs');
(0, fs_extra_1.moveSync)((0, path_1.join)(dir, 'rollup.config.js'), (0, path_1.join)(dir, 'rollup.config.mjs'));
}
if (updatePrettierJava) {
if (!packageJsonText.includes('--plugin=prettier-plugin-java')) {
packageJsonText = packageJsonText.replace('"prettier \\"**/*.{css,html,ts,js,java}\\"', '"prettier \\"**/*.{css,html,ts,js,java}\\" --plugin=prettier-plugin-java');
}
let prettierIgnoreText = readFile(prettierIgnore);
const gitIgnoreText = readFile(gitIgnore);
if (gitIgnoreText && prettierIgnoreText) {
if (gitIgnoreText.includes('build')) {
prettierIgnoreText = prettierIgnoreText.replace(`build\n`, '');
}
if (gitIgnoreText.includes('dist')) {
prettierIgnoreText = prettierIgnoreText.replace(`dist\n`, '');
}
if (!prettierIgnoreText || prettierIgnoreText === `\n`) {
(0, fs_extra_1.removeSync)(prettierIgnore);
}
else {
(0, fs_extra_1.writeFileSync)(prettierIgnore, prettierIgnoreText, 'utf-8');
}
}
}
(0, fs_extra_1.writeFileSync)(packageJson, packageJsonText, 'utf-8');
rimraf_1.rimraf.sync((0, path_1.join)(dir, 'node_modules/@capacitor'));
rimraf_1.rimraf.sync((0, path_1.join)(dir, 'package-lock.json'));
try {
await (0, subprocess_1.runCommand)('npm', ['install'], {
...opts,
cwd: dir,
});
}
catch (e) {
log_1.logger.warn('npm install failed, please, install the dependencies using your package manager of choice');
}
if (pluginJSON.capacitor?.android?.src) {
const androidDir = (0, path_1.resolve)(dir, pluginJSON.capacitor.android.src);
if (await (0, fs_extra_1.pathExists)(androidDir)) {
log_1.logger.info('Updating Android files');
log_1.logger.info('Updating gradle files');
await (0, subprocess_1.runCommand)('./gradlew', ['wrapper', '--distribution-type', 'all', '--gradle-version', gradleVersion, '--warning-mode', 'all'], {
...opts,
cwd: androidDir,
});
// run twice as first run only updates the properties file
await (0, subprocess_1.runCommand)('./gradlew', ['wrapper', '--distribution-type', 'all', '--gradle-version', gradleVersion, '--warning-mode', 'all'], {
...opts,
cwd: androidDir,
});
const variablesAndClasspaths = {
variables: variables,
'com.android.tools.build:gradle': AGPVersion,
'com.google.gms:google-services': gmsVersion,
};
await updateBuildGradle((0, path_1.join)(androidDir, 'build.gradle'), variablesAndClasspaths);
}
}
if (pluginJSON.capacitor?.ios?.src) {
const iosDir = (0, path_1.resolve)(dir, pluginJSON.capacitor.ios.src);
if (await (0, fs_extra_1.pathExists)(iosDir)) {
log_1.logger.info('Updating iOS files');
await updateFile((0, path_1.join)(iosDir, 'Plugin.xcodeproj', 'project.pbxproj'), 'IPHONEOS_DEPLOYMENT_TARGET = ', ';', '14.0');
await updateFile((0, path_1.join)(iosDir, 'Podfile'), `platform :ios, '`, `'`, '14.0');
await updateFile((0, path_1.join)(dir, 'Package.swift'), '[.iOS(.v', ')],', '14');
await updateFile((0, path_1.join)(dir, 'Package.swift'), '.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git",', ')', ` from: "${coreVersion}"`);
await updatePodspec(dir, pluginJSON);
}
}
log_1.logger.info('Plugin migrated to Capacitor 7!');
};
exports.run = run;
function updatePodspec(dir, pluginJSON) {
const podspecFile = pluginJSON.files.find((file) => file.includes('.podspec'));
let txt = readFile((0, path_1.join)(dir, podspecFile));
if (!txt) {
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'`);
(0, fs_extra_1.writeFileSync)(podspecFile, txt, { encoding: 'utf-8' });
}
async function updateBuildGradle(filename, variablesAndClasspaths) {
let gradleFile = readFile(filename);
if (!gradleFile) {
return;
}
gradleFile = gradleFile.replaceAll(' = ', ' = ');
log_1.logger.info('Updating build.gradle');
gradleFile = setAllStringIn(gradleFile, `sourceCompatibility JavaVersion.`, `\n`, `VERSION_21`);
gradleFile = setAllStringIn(gradleFile, `targetCompatibility JavaVersion.`, `\n`, `VERSION_21`);
const neededDeps = {
'com.android.tools.build:gradle': variablesAndClasspaths['com.android.tools.build:gradle'],
'com.google.gms:google-services': variablesAndClasspaths['com.google.gms:google-services'],
};
for (const dep of Object.keys(neededDeps)) {
if (gradleFile.includes(`classpath '${dep}`)) {
const semver = await Promise.resolve().then(() => __importStar(require('semver')));
const firstIndex = gradleFile.indexOf(dep) + dep.length + 1;
const existingVersion = '' + gradleFile.substring(firstIndex, gradleFile.indexOf("'", firstIndex));
if (semver.gte(neededDeps[dep], existingVersion)) {
gradleFile = setAllStringIn(gradleFile, `classpath '${dep}:`, `'`, neededDeps[dep]);
log_1.logger.info(`Set ${dep} = ${neededDeps[dep]}.`);
}
}
}
for (const [dep, depValue] of Object.entries(variablesAndClasspaths.variables)) {
let depString = `${dep} = project.hasProperty('${dep}') ? rootProject.ext.${dep} : '`;
let depValueString = depValue;
let endString = `'`;
if (typeof depValue === 'number') {
depString = `project.hasProperty('${dep}') ? rootProject.ext.${dep} : `;
depValueString = depValue.toString();
endString = '\n';
}
if (gradleFile.includes(depString) && typeof depValueString === 'string') {
const semver = await Promise.resolve().then(() => __importStar(require('semver')));
const firstIndex = gradleFile.indexOf(depString) + depString.length;
const existingVersion = '' + gradleFile.substring(firstIndex, gradleFile.indexOf(endString, firstIndex));
if ((semver.valid(depValueString) && semver.gte(depValueString, existingVersion)) ||
(!semver.valid(depValueString) && depValue > Number(existingVersion))) {
gradleFile = setAllStringIn(gradleFile, depString, endString, depValueString);
log_1.logger.info(`Set ${dep} = ${depValueString}.`);
}
}
}
gradleFile = setAllStringIn(gradleFile, `ext.kotlin_version = `, `\n`, `project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '${kotlinVersion}'`);
gradleFile = setAllStringIn(gradleFile, `implementation "org.jetbrains.kotlin:kotlin-stdlib`, `"`, `:$kotlin_version`);
(0, fs_extra_1.writeFileSync)(filename, gradleFile, 'utf-8');
}
function readFile(filename) {
try {
if (!(0, fs_extra_1.existsSync)(filename)) {
log_1.logger.warn(`Unable to find ${filename}.`);
return;
}
return (0, fs_extra_1.readFileSync)(filename, 'utf-8');
}
catch (err) {
log_1.logger.warn(`Unable to read ${filename}. Verify it is not already open. ${err}`);
}
}
function setAllStringIn(data, start, end, replacement) {
let position = 0;
let result = data;
let replaced = true;
while (replaced) {
const foundIdx = result.indexOf(start, position);
if (foundIdx == -1) {
replaced = false;
}
else {
const idx = foundIdx + start.length;
position = idx + replacement.length;
result = result.substring(0, idx) + replacement + result.substring(result.indexOf(end, idx));
}
}
return result;
}
async function updateFile(filename, textStart, textEnd, replacement, skipIfNotFound) {
const path = filename;
let txt = readFile(path);
if (!txt) {
return false;
}
if (txt.includes(textStart)) {
if (replacement) {
txt = setAllStringIn(txt, textStart, textEnd, replacement);
(0, fs_extra_1.writeFileSync)(path, txt, { encoding: 'utf-8' });
}
else {
// Replacing in code so we need to count the number of brackets to find the end of the function in swift
const lines = txt.split('\n');
let replaced = '';
let keep = true;
let brackets = 0;
for (const line of lines) {
if (line.includes(textStart)) {
keep = false;
}
if (!keep) {
brackets += (line.match(/{/g) || []).length;
brackets -= (line.match(/}/g) || []).length;
if (brackets == 0) {
keep = true;
}
}
else {
replaced += line + '\n';
}
}
(0, fs_extra_1.writeFileSync)(path, replaced, { encoding: 'utf-8' });
}
return true;
}
else if (!skipIfNotFound) {
log_1.logger.warn(`Unable to find "${textStart}" in ${filename}. Try updating it manually`);
}
return false;
}