UNPKG

@capacitor/plugin-migration-v6-to-v7

Version:

Utility to help migrate Capacitor 6 plugins to Capacitor 7

299 lines (298 loc) 13.6 kB
"use strict"; 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; }