From cb98244ef163806563aedf4ba3cc841bded8350b Mon Sep 17 00:00:00 2001 From: Damian Tarnawsky <84595830+dtarnawsky@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:19:23 -0800 Subject: [PATCH] 1.62 (#98) * fix: task queueing issue when a dialog is left to timeout * fix: undefined tooltip on ios and android run * feat: Improve output of a security audit * chore: roadmap * chore: docs * feat: Strip extraneous angular warnings on a build * feat: Root folder is also listed as a project for folder based mono-repos * feat: Tooltips for mono-repo projects are now the path for the project * feat: Update `@angular-eslint/*` packages when an Angular migration is done --- CHANGELOG.md | 11 +- package-lock.json | 4 +- package.json | 2 +- roadmap.md | 13 +- src/analyze-size.ts | 8 +- src/android-debug-list.ts | 13 +- src/angular-generate.ts | 8 +- src/audit.ts | 92 ++++++++++++- src/capacitor-build.ts | 16 ++- src/capacitor-configure.ts | 51 +++++--- src/capacitor-migrate.ts | 60 +++++---- src/capacitor-pwa.ts | 9 +- src/ionic-export.ts | 8 +- src/ionic-projects-provider.ts | 2 +- src/log-settings.ts | 4 +- src/monorepo.ts | 10 +- src/recommend.ts | 94 ++++++-------- src/rules-angular-json.ts | 40 +++--- src/rules-angular-migrate.ts | 27 ++-- src/rules-angular-toolkit.ts | 13 +- src/rules-browserslist.ts | 30 +++-- src/rules-capacitor-plugins.ts | 12 +- src/rules-capacitor.ts | 230 +++++++++++++++++---------------- src/splash-icon.ts | 28 ++-- src/tip.ts | 27 +++- src/update-minor.ts | 10 +- src/utilities.ts | 30 +++-- 27 files changed, 538 insertions(+), 314 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46e0a9..0faef99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Changelog +### Version 1.62 + +- Fix task queueing issue when a dialog is left to timeout +- Improved output of a security audit (`Configuration` > `Security Audit`) +- Strip extraneous angular warnings on a build +- Root folder is also listed as a project for folder based mono-repos +- Tooltips for mono-repo projects are now the path for the project +- Update `@angular-eslint/*` packages when an Angular migration is done + ### Version 1.61 - Fix cocoapods version detection (if a project had previously detected an older version) @@ -11,7 +20,7 @@ ### Version 1.60 -- Update rule for onesignal-cordova-plugin conflicting with Auth Connect +- Update rule for `onesignal-cordova-plugin` conflicting with Auth Connect - Recommendation to move browserslist file into package.json to reduce config files in a project ### Version 1.59 diff --git a/package-lock.json b/package-lock.json index 7807dfe..0320329 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ionic", - "version": "1.61.0", + "version": "1.62.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ionic", - "version": "1.61.0", + "version": "1.62.0", "license": "MIT", "dependencies": { "-": "^0.0.1", diff --git a/package.json b/package.json index a697f9b..202c962 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ionic", "displayName": "Ionic", "description": "Official extension for Ionic and Capacitor development", - "version": "1.61.0", + "version": "1.62.0", "icon": "media/ionic.png", "publisher": "Ionic", "keywords": [ diff --git a/roadmap.md b/roadmap.md index 51a789e..dd43a57 100644 --- a/roadmap.md +++ b/roadmap.md @@ -1,10 +1,17 @@ # Roadmap -- Fix queuing issue when user waits for a prompt - TODO: add pwa integration +- Fix: The bundledWebRuntime configuration option has been deprecated. Can be safely deleted. +- Generate twitter and og metatags +- Generate social and favicon +- Avoid sharp and @capacitor/assets +- Show the root package for projects - ie scripts etc - Angular projects that use IonicModule (eg `import { IonicModule } from '@ionic/angular';`) and `@ionic/angular/standalone` or `import { send } from 'ionicons/icons';` will break. Check code for it? +- Customers dont really understand the upgrade process in packages/plugins - looking in the "plugins" windows: +- Need to recommend upgrading minor versions of Capacitor on a regular basis (eg monthly) +- Need to recommend upgrading minor versions of Capacitor plugins on a regular basis (eg monthly) - Angular standalone templates when a component is added (eg `ion-header`) will error until imported correctly. Add an auto-import feature? - +- For Capacitor 5 check deployment target is ios >= 13 - Find: Search for all pages, components, routes etc and put in a search box to speed up opens - Alt+T: Toggle between html/scss and ts @@ -24,7 +31,6 @@ Check `main.ts` for `provideIonicAngular` and apply this if missing - As --force is used with Angular migrations there may be peer dependency errors that need resolution afterwards. Need a peer dep resolver. - Update docs on capacitorjs.com -- Fix: The bundledWebRuntime configuration option has been deprecated. Can be safely deleted. - Recommendation for experimental migration to Angular standalone components - Recommendation for experimental migration to Angular built-in control flow syntax - Recommendation of migration to @ionic/angular (major versions) and link to migration doc @@ -90,7 +96,6 @@ Check `main.ts` for `provideIonicAngular` and apply this if missing - Show git remote url somewhere (`git config --get remote.origin.url`) - (feat) Debugger for iOS (add breakpoints, inspection etc) - (feat) info.plist editing -- (feat) Twitter to RSS feed - pull news into plugin ?? - (1) Getting devices takes some time to run the first time. Make sure logging goes to Output window and if taking > 5 seconds then give user feedback that it may take time - (2) If you sync but the build didn't work then show suitable error (or trigger build) - (2) If a project has not been built and you try running on ios/android it could build for you beforehand diff --git a/src/analyze-size.ts b/src/analyze-size.ts index c72128c..ead035a 100644 --- a/src/analyze-size.ts +++ b/src/analyze-size.ts @@ -7,14 +7,16 @@ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from ' import { ionicBuild } from './ionic-build'; import { MonoRepoType } from './monorepo'; import { ViewColumn, window } from 'vscode'; +import { QueueFunction } from './tip'; /** * Generates a readable analysis of Javascript bundle sizes * Uses source-map-explorer on a production build of the app with source maps turned on * @param {Project} project */ -export async function analyzeSize(project: Project) { +export async function analyzeSize(queueFunction: QueueFunction, project: Project) { const dist = project.getDistFolder(); + queueFunction(); await showProgress('Generating Project Statistics', async () => { let previousValue; try { @@ -44,12 +46,12 @@ export async function analyzeSize(project: Project) { analyzeResults( analyzeBundles(stripJSON(result.output, '{')), 'Bundle Analysis', - 'Size of Javascript bundles for your code and 3rd party packages.' + 'Size of Javascript bundles for your code and 3rd party packages.', ) + analyzeResults( analyzeAssets(dist, project.projectFolder()), 'Asset Analysis', - 'Size of assets in your distribution folder.' + 'Size of assets in your distribution folder.', ); showWindow(project.projectFolder(), html); writeIonic('Launched project statistics window.'); diff --git a/src/android-debug-list.ts b/src/android-debug-list.ts index d323e1a..3377a9b 100644 --- a/src/android-debug-list.ts +++ b/src/android-debug-list.ts @@ -5,11 +5,11 @@ import { Device, WebView } from './android-debug-models'; import { CommandName } from './command-name'; import { ionicState } from './ionic-tree-provider'; import { Recommendation } from './recommendation'; -import { Tip, TipType } from './tip'; +import { QueueFunction, Tip, TipType } from './tip'; export async function getAndroidWebViewList( hasCapacitorAndroid: boolean, - wwwFolder: string + wwwFolder: string, ): Promise { if (ionicState.refreshDebugDevices) { ionicState.refreshDebugDevices = false; @@ -29,10 +29,10 @@ export async function getAndroidWebViewList( `${webview.packageName}`, TreeItemCollapsibleState.None, getCommand(), - undefined + undefined, ); r.setIcon('debug'); - r.tip = new Tip(undefined, undefined, TipType.Run).setAction(debug, device, webview, wwwFolder).doNotWait(); + r.tip = new Tip(undefined, undefined, TipType.Run).setQueuedAction(debug, device, webview, wwwFolder).doNotWait(); r.command.arguments = [r]; result.push(r); } @@ -43,7 +43,7 @@ export async function getAndroidWebViewList( device.product, TreeItemCollapsibleState.None, getCommand(), - undefined + undefined, ); r.setIcon('android'); result.push(r); @@ -52,7 +52,8 @@ export async function getAndroidWebViewList( return result; } -async function debug(device: Device, webview: WebView, wwwfolder: string): Promise { +async function debug(queueFunction: QueueFunction, device: Device, webview: WebView, wwwfolder: string): Promise { + queueFunction(); debugAndroid(webview.packageName, wwwfolder); return; } diff --git a/src/angular-generate.ts b/src/angular-generate.ts index 26c0943..21d37a7 100644 --- a/src/angular-generate.ts +++ b/src/angular-generate.ts @@ -6,14 +6,20 @@ import { join } from 'path'; import { existsSync } from 'fs'; import { isGreaterOrEqual } from './analyzer'; import { window } from 'vscode'; +import { QueueFunction } from './tip'; -export async function angularGenerate(project: Project, angularType: string): Promise { +export async function angularGenerate( + queueFunction: QueueFunction, + project: Project, + angularType: string, +): Promise { let name = await window.showInputBox({ title: `New Angular ${angularType}`, placeHolder: `Enter name for new ${angularType}`, }); if (!name || name.length < 1) return; + queueFunction(); // CREATE src/app/test2/test2.component.ts try { diff --git a/src/audit.ts b/src/audit.ts index 358a0ae..8d7620b 100644 --- a/src/audit.ts +++ b/src/audit.ts @@ -2,10 +2,14 @@ import { window } from 'vscode'; import { clearOutput, write, writeError, writeIonic } from './logging'; import { Project } from './project'; import { getRunOutput, run, showProgress, stripJSON } from './utilities'; +import { QueueFunction } from './tip'; +import { getAllPackageNames } from './analyzer'; -export async function audit(project: Project): Promise { +export async function audit(queueFunction: QueueFunction, project: Project): Promise { try { + queueFunction(); clearOutput(); + let vulnerabilities: SecurityVulnerability[] = []; await showProgress('Auditing project dependencies...', async () => { let folder = project.projectFolder(); if (project.monoRepo?.nodeModulesAtRoot) { @@ -14,7 +18,15 @@ export async function audit(project: Project): Promise { const data = await getRunOutput('npm audit --json', folder); try { const audit: Audit = JSON.parse(stripJSON(data, '{')); - completeAudit(project, audit); + const dependencies = getAllPackageNames(); + vulnerabilities = analyzeAudit(dependencies, audit); + setTimeout(async () => { + if (vulnerabilities.length > 0) { + await checkAuditFix(vulnerabilities, project); + } else { + writeIonic(`No security vulnerabilities were found using npm audit.`); + } + }, 1); } catch (error) { writeError('npm audit --json failed with:'); writeError(data); @@ -26,11 +38,68 @@ export async function audit(project: Project): Promise { return; } +export interface SecurityVulnerability { + name: string; + severity: string; + url: string; + title: string; +} + +function analyzeAudit(dependencies: string[], audit: Audit): SecurityVulnerability[] { + const result: SecurityVulnerability[] = []; + for (const name of Object.keys(audit.vulnerabilities)) { + const v: Vulnerability = audit.vulnerabilities[name]; + + if (dependencies.includes(name)) { + const source = drillDown(name, audit); + result.push({ + name, + severity: v.severity, + title: source ? source.title : '', + url: source ? source.url : '', + }); + } + } + return result; +} + +function drillDown(name: string, audit: Audit): Source | undefined { + for (const source of audit.vulnerabilities[name].via) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (!(source as any).title) { + return drillDown(source as string, audit); + } else { + const src: Source = source as Source; + return src; + } + } +} + +async function checkAuditFix(vulnerabilities: SecurityVulnerability[], project: Project) { + for (const vulnerability of vulnerabilities) { + write( + `${colorSeverity(vulnerability.severity)} ${vulnerability.severity} - ${vulnerability.name} - ${ + vulnerability.title + } (${vulnerability.url})`, + ); + } + const response = await window.showWarningMessage( + `Security vulnerabilities were found in your project. Do you want to attempt to fix them?`, + 'Yes', + 'Cancel', + ); + if (response === 'Yes') { + clearOutput(); + write('> npm audit fix'); + await run(project.projectFolder(), 'npm audit fix', undefined, [], [], undefined, undefined, undefined, false); + } +} + async function completeAudit(project: Project, audit: Audit) { const severities = ['critical', 'high', 'moderate', 'low']; const types = ['direct', 'indirect']; writeIonic( - `There are ${audit.metadata.vulnerabilities.total} security vulnerabilities in your projects ${audit.metadata.dependencies.total} dependencies` + `There are ${audit.metadata.vulnerabilities.total} security vulnerabilities in your projects ${audit.metadata.dependencies.total} dependencies`, ); for (const type of types) { if (type == 'indirect' && audit.metadata.vulnerabilities.total > 0) { @@ -60,7 +129,7 @@ async function completeAudit(project: Project, audit: Audit) { const response = await window.showWarningMessage( `${audit.metadata.vulnerabilities.total} security vulnerabilities were found in your project. Do you want to attempt to fix them?`, 'Yes', - 'Cancel' + 'Cancel', ); if (response === 'Yes') { clearOutput(); @@ -70,6 +139,21 @@ async function completeAudit(project: Project, audit: Audit) { } } +function colorSeverity(severity: string | undefined): string { + switch (severity) { + case 'critical': + return '🔴'; + case 'high': + return '🟠'; + case 'moderate': + return '🟡'; + case 'low': + return '⚪'; + default: + return '-'; + } +} + interface Vulnerability { name: string; severity: string; diff --git a/src/capacitor-build.ts b/src/capacitor-build.ts index 7fbf282..b0cb931 100644 --- a/src/capacitor-build.ts +++ b/src/capacitor-build.ts @@ -11,12 +11,13 @@ import { npx, PackageManager } from './node-commands'; import { exec } from 'child_process'; import { getCapacitorConfigureFilename, writeCapacitorConfig } from './capacitor-config-file'; import { window } from 'vscode'; +import { QueueFunction } from './tip'; /** * Capacitor build command * @param {Project} project */ -export async function capacitorBuild(project: Project) { +export async function capacitorBuild(queueFunction: QueueFunction, project: Project) { if (!isGreaterOrEqual('@capacitor/cli', '4.4.0')) { await window.showErrorMessage('This option is only available in Capacitor version 4.4.0 and above.'); return; @@ -46,6 +47,7 @@ export async function capacitorBuild(project: Project) { } try { + queueFunction(); const command = capBuildCommand(project, platform, args, settings); writeIonic(command); const results: RunResults = { output: '', success: false }; @@ -68,7 +70,7 @@ async function openPortal(platform: CapacitorPlatform) { const selection = await window.showInformationMessage( `Do you want to open the ${platform == CapacitorPlatform.ios ? 'Apple Developer Portal?' : 'Google Play Console?'}`, 'Open', - 'Exit' + 'Exit', ); if (selection == 'Open') { openUri(uri); @@ -78,7 +80,7 @@ async function openPortal(platform: CapacitorPlatform) { async function verifySettings( project: Project, platform: CapacitorPlatform, - settings: KeyStoreSettings + settings: KeyStoreSettings, ): Promise { if (platform == CapacitorPlatform.ios) return settings; @@ -87,7 +89,7 @@ async function verifySettings( 'An Android Keystore file is required. You can create one in Android Studio (Build > Generate Signed Bundle).', 'Select Keystore File', 'Open Android Studio', - 'Exit' + 'Exit', ); if (!selection || selection == 'Exit') { return undefined; @@ -146,7 +148,7 @@ function capBuildCommand( project: Project, platform: CapacitorPlatform, args: string, - settings: KeyStoreSettings + settings: KeyStoreSettings, ): string { switch (project.repoType) { case MonoRepoType.none: @@ -168,7 +170,7 @@ function capCLIBuild( platform: CapacitorPlatform, packageManager: PackageManager, args: string, - settings: KeyStoreSettings + settings: KeyStoreSettings, ): string { if (platform == CapacitorPlatform.android) { if (settings.keyAlias) args += ` --keystorealias="${settings.keyAlias}"`; @@ -229,7 +231,7 @@ function writeConfig(project: Project, settings: KeyStoreSettings) { keystoreAlias: '', } } - };` + };`, ); } writeFileSync(filename, data); diff --git a/src/capacitor-configure.ts b/src/capacitor-configure.ts index 2d4ff05..ccd5679 100644 --- a/src/capacitor-configure.ts +++ b/src/capacitor-configure.ts @@ -1,7 +1,7 @@ import { MobileProject, MobileProjectConfig } from '@trapezedev/project'; import { CapacitorProjectState } from './cap-project'; import { Project } from './project'; -import { Tip, TipType } from './tip'; +import { QueueFunction, Tip, TipType } from './tip'; import { channelShow, getStringFrom, setStringIn } from './utilities'; import { CapProjectCache } from './context-variables'; import { join } from 'path'; @@ -36,16 +36,16 @@ export async function reviewCapacitorConfig(project: Project, context: Extension const bundleId = state.androidBundleId ? state.androidBundleId : state.iosBundleId; const tip = new Tip('Bundle Id', bundleId, TipType.None); - tip.setAction(setBundleId, bundleId, project, project.folder); + tip.setQueuedAction(setBundleId, bundleId, project, project.folder); project.add(tip); } else { // Bundle Ids different const tip = new Tip('Android Bundle Id', state.androidBundleId, TipType.None); - tip.setAction(setBundleId, state.androidBundleId, project, project.folder, NativePlatform.AndroidOnly); + tip.setQueuedAction(setBundleId, state.androidBundleId, project, project.folder, NativePlatform.AndroidOnly); project.add(tip); const tip2 = new Tip('iOS Bundle Id', state.iosBundleId, TipType.None); - tip2.setAction(setBundleId, state.iosBundleId, project, project.folder, NativePlatform.iOSOnly); + tip2.setQueuedAction(setBundleId, state.iosBundleId, project, project.folder, NativePlatform.iOSOnly); project.add(tip2); } @@ -53,15 +53,15 @@ export async function reviewCapacitorConfig(project: Project, context: Extension if (state.androidDisplayName == state.iosDisplayName || !state.iosDisplayName || !state.androidDisplayName) { const displayName = state.androidDisplayName ? state.androidDisplayName : state.iosDisplayName; const tip = new Tip('Display Name', displayName, TipType.None); - tip.setAction(setDisplayName, displayName, project, project.folder); + tip.setQueuedAction(setDisplayName, displayName, project, project.folder); project.add(tip); } else { const tip = new Tip('Android Display Name', state.androidDisplayName, TipType.None); - tip.setAction(setDisplayName, state.androidDisplayName, project, project.folder, NativePlatform.AndroidOnly); + tip.setQueuedAction(setDisplayName, state.androidDisplayName, project, project.folder, NativePlatform.AndroidOnly); project.add(tip); const tip2 = new Tip('iOS Display Name', state.iosDisplayName, TipType.None); - tip2.setAction(setDisplayName, state.iosDisplayName, project, project.folder, NativePlatform.iOSOnly); + tip2.setQueuedAction(setDisplayName, state.iosDisplayName, project, project.folder, NativePlatform.iOSOnly); project.add(tip2); } @@ -69,15 +69,15 @@ export async function reviewCapacitorConfig(project: Project, context: Extension if (state.androidVersion == state.iosVersion || !state.iosVersion || !state.androidVersion) { const version = state.androidVersion ? state.androidVersion : state.iosVersion; const tip = new Tip('Version Number', version?.toString(), TipType.None); - tip.setAction(setVersion, version, project); + tip.setQueuedAction(setVersion, version, project); project.add(tip); } else { const tip = new Tip('Android Version Number', state.androidVersion, TipType.None); - tip.setAction(setVersion, state.androidVersion, project, NativePlatform.AndroidOnly); + tip.setQueuedAction(setVersion, state.androidVersion, project, NativePlatform.AndroidOnly); project.add(tip); const tip2 = new Tip('iOS Version Number', state.iosVersion, TipType.None); - tip2.setAction(setVersion, state.iosVersion, project, NativePlatform.iOSOnly); + tip2.setQueuedAction(setVersion, state.iosVersion, project, NativePlatform.iOSOnly); project.add(tip2); } @@ -85,15 +85,15 @@ export async function reviewCapacitorConfig(project: Project, context: Extension if (state.androidBuild == state.iosBuild || !state.iosBuild || !state.androidBuild) { const build = state.androidBuild ? state.androidBuild : state.iosBuild; const tip = new Tip('Build Number', build?.toString(), TipType.None); - tip.setAction(setBuild, build, project); + tip.setQueuedAction(setBuild, build, project); project.add(tip); } else { const tip = new Tip('Android Build Number', state.androidBuild?.toString(), TipType.None); - tip.setAction(setBuild, state.androidBuild, project, NativePlatform.AndroidOnly); + tip.setQueuedAction(setBuild, state.androidBuild, project, NativePlatform.AndroidOnly); project.add(tip); const tip2 = new Tip('iOS Build Number', state.iosBuild?.toString(), TipType.None); - tip2.setAction(setBuild, state.iosBuild, project, NativePlatform.iOSOnly); + tip2.setQueuedAction(setBuild, state.iosBuild, project, NativePlatform.iOSOnly); project.add(tip2); } @@ -201,7 +201,13 @@ async function getCapacitorProjectState(prj: Project, context: ExtensionContext) * @param {string} folder Folder for the project * @param {NativePlatform} platform Whether iOS or Android only (default both) */ -async function setBundleId(bundleId: string, prj: Project, folder: string, platform: NativePlatform) { +async function setBundleId( + queueFunction: QueueFunction, + bundleId: string, + prj: Project, + folder: string, + platform: NativePlatform, +) { const newBundleId = await window.showInputBox({ title: 'Application Bundle Id', placeHolder: bundleId, @@ -218,7 +224,7 @@ async function setBundleId(bundleId: string, prj: Project, folder: string, platf if (!newBundleId) { return; // User cancelled } - + queueFunction(); const project = await getCapacitorProject(prj); if (project?.ios && platform != NativePlatform.AndroidOnly) { @@ -285,7 +291,7 @@ function clearCapProjectCache() { * @param {string} version * @param {NativePlatform} platform Whether to apply for iOS only, Android only or both (default) */ -async function setVersion(version: string, prj: Project, platform: NativePlatform) { +async function setVersion(queueFunction: QueueFunction, version: string, prj: Project, platform: NativePlatform) { const newVersion = await window.showInputBox({ title: 'Application Version Number', placeHolder: version, @@ -303,6 +309,7 @@ async function setVersion(version: string, prj: Project, platform: NativePlatfor return; // User cancelled } + queueFunction(); const project = await getCapacitorProject(prj); if (project?.ios && platform != NativePlatform.AndroidOnly) { @@ -327,7 +334,7 @@ async function setVersion(version: string, prj: Project, platform: NativePlatfor * @param {CapacitorProject} project The Capacitor project * @param {NativePlatform} platform Whether to apply on iOS only, Android Only or both (default) */ -async function setBuild(build: string, prj: Project, platform: NativePlatform) { +async function setBuild(queueFunction: QueueFunction, build: string, prj: Project, platform: NativePlatform) { const newBuild = await window.showInputBox({ title: 'Application Build Number', placeHolder: build, @@ -345,6 +352,7 @@ async function setBuild(build: string, prj: Project, platform: NativePlatform) { return; // User cancelled } + queueFunction(); const project = await getCapacitorProject(prj); if (project?.ios && platform != NativePlatform.AndroidOnly) { @@ -369,7 +377,13 @@ async function setBuild(build: string, prj: Project, platform: NativePlatform) { * @param {string} folder Folder for the project * @param {NativePlatform} platform Whether to apply to iOS only, Android only or both (default) */ -async function setDisplayName(currentDisplayName: string, prj: Project, folder: string, platform: NativePlatform) { +async function setDisplayName( + queueFunction: QueueFunction, + currentDisplayName: string, + prj: Project, + folder: string, + platform: NativePlatform, +) { const displayName = await window.showInputBox({ title: 'Application Display Name', placeHolder: currentDisplayName, @@ -380,6 +394,7 @@ async function setDisplayName(currentDisplayName: string, prj: Project, folder: return; // User cancelled } + queueFunction(); const project = await getCapacitorProject(prj); console.log(`Display name changed to ${displayName}`); diff --git a/src/capacitor-migrate.ts b/src/capacitor-migrate.ts index 738e58c..2e2bff9 100644 --- a/src/capacitor-migrate.ts +++ b/src/capacitor-migrate.ts @@ -16,8 +16,13 @@ import { CapacitorPlatform } from './capacitor-platform'; import { checkPeerDependencies, PeerReport } from './peer-dependencies'; import { removeNodeModules } from './advanced-actions'; import { window } from 'vscode'; +import { QueueFunction } from './tip'; -export async function migrateCapacitor5(project: Project, currentVersion: string): Promise { +export async function migrateCapacitor5( + queueFunction: QueueFunction, + project: Project, + currentVersion: string, +): Promise { const coreVersion = '5'; const versionTitle = '5'; const versionFull = '5.0.0'; @@ -29,7 +34,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string const res = await window.showInformationMessage( `Android Studio Flamingo (2022.2.1) is the minimum version needed for Capacitor ${versionTitle} (It comes with Java 17 and Gradle 8). Choose Android Studio > Check for Updates.`, openStudio, - 'Continue...' + 'Continue...', ); if (res === openStudio) { await run( @@ -38,7 +43,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string undefined, [], undefined, - undefined + undefined, ); return; } @@ -50,7 +55,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string const result = await window.showInformationMessage( `Your version of Java is ${version} but version 17 is the minimum required. Please check your JAVA_HOME path and ensure it is using JDK Version 17. You may need to restart VS Code after making this change.`, 'OK', - 'Continue' + 'Continue', ); if (result !== 'Continue') { return; @@ -60,6 +65,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string clearOutput(); showOutput(); + queueFunction(); let report: PeerReport; await showProgress(`Checking plugins in your project...`, async () => { report = await checkPeerDependencies(project.folder, '@capacitor/core', versionFull); @@ -94,10 +100,10 @@ export async function migrateCapacitor5(project: Project, currentVersion: string const result = await window.showErrorMessage( `There ${plural('are', report.incompatible.length)} ${pluralize( 'plugin', - report.incompatible.length + report.incompatible.length, )} in your project that does not work with Capacitor ${versionTitle}. Filing an issue with the author is recommended.`, `Continue`, - 'Exit' + 'Exit', ); if (result != 'Continue') { return; @@ -107,7 +113,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string const result = await window.showInformationMessage( `Capacitor ${versionTitle} sets a deployment target of iOS 13 and Android 13 (SDK 33).`, `Migrate to v${versionTitle}`, - 'Ignore' + 'Ignore', ); if (result == 'Ignore') { return ActionResult.Ignore; @@ -133,7 +139,7 @@ export async function migrateCapacitor5(project: Project, currentVersion: string try { const result = await getRunOutput( logCmd(`npx cap migrate --noprompt --packagemanager=${manager}`), - project.projectFolder() + project.projectFolder(), ); write(result); if (result.includes('[error] npm install failed. Try deleting node_modules')) { @@ -212,7 +218,11 @@ export interface AndroidStudioInfo { version: string; } -export async function migrateCapacitor(project: Project, currentVersion: string): Promise { +export async function migrateCapacitor( + queueFunction: QueueFunction, + project: Project, + currentVersion: string, +): Promise { const coreVersion = '^4.0.1'; const pluginVersion = '^4.0.1'; @@ -224,7 +234,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) const result = await window.showInformationMessage( `Capacitor 4 sets a deployment target of iOS 13 and Android 12 (SDK 32). ${warning}`, 'Migrate to v4', - 'Ignore' + 'Ignore', ); if (result == 'Ignore') { return ActionResult.Ignore; @@ -232,7 +242,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) if (!result) { return; } - + queueFunction(); await showProgress(`Migrating to Capacitor 4`, async () => { try { let replaceStorage = false; @@ -272,8 +282,8 @@ export async function migrateCapacitor(project: Project, currentVersion: string) '@capacitor/toast', ], coreVersion, - pluginVersion - ) + pluginVersion, + ), ); if (replaceStorage) { @@ -288,7 +298,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) join('ios', 'App', 'App.xcodeproj', 'project.pbxproj'), 'IPHONEOS_DEPLOYMENT_TARGET = ', ';', - '13.0' + '13.0', ); // Update Podfile to 13.0 @@ -371,7 +381,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) `${variable} = '`, `'`, variables[variable].toString(), - true + true, ) ) { if ( @@ -381,7 +391,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) `${variable} = `, `\n`, addQuotes(variables[variable].toString()), - true + true, ) ) { // Add variables if they are in the core list of required ones @@ -398,7 +408,7 @@ export async function migrateCapacitor(project: Project, currentVersion: string) updateVariablesGradle( join(project.projectFolder(), 'android', 'variables.gradle'), variable, - variables[variable].toString() + variables[variable].toString(), ); } } @@ -458,15 +468,15 @@ function writeBreakingChanges() { if (broken.length > 0) { writeIonic( `IMPORTANT: Review https://capacitorjs.com/docs/updating/4-0#plugins for breaking changes in these plugins that you use: ${broken.join( - ', ' - )}.` + ', ', + )}.`, ); } else { writeIonic('IMPORTANT: Review https://capacitorjs.com/docs/updating/4-0 for optional manual updates.'); } if (exists('@capacitor/android')) { writeIonic( - 'Warning: The Android Gradle plugin was updated and it requires Java 11 to run (included with Android Studio). You may need to select this in Android Studio (Preferences > Build, Execution, Deployment > Build Tools > Gradle).' + 'Warning: The Android Gradle plugin was updated and it requires Java 11 to run (included with Android Studio). You may need to select this in Android Studio (Preferences > Build, Execution, Deployment > Build Tools > Gradle).', ); } } @@ -571,7 +581,7 @@ function updateMainActivity(path: string) { const eindex = data.lastIndexOf('.class);') + 8; data = data.replace( data.substring(bindex, eindex), - `${data.substring(bindex, eindex)}\n${' '.repeat(linePadding) + superLine.padStart(linePadding)}` + `${data.substring(bindex, eindex)}\n${' '.repeat(linePadding) + superLine.padStart(linePadding)}`, ); } } @@ -619,7 +629,7 @@ function patchPodFile(filename: string) { if (!replaced.includes('assertDeploymentTarget(installer)')) { replaced = replaced.replace( `post_install do |installer|`, - `post_install do |installer|\n assertDeploymentTarget(installer)\n` + `post_install do |installer|\n assertDeploymentTarget(installer)\n`, ); } } @@ -681,7 +691,7 @@ function updateGradleWrapper(filename: string) { 'distributionUrl=', '\n', // eslint-disable-next-line no-useless-escape - `https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip` + `https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip`, ); if (txt != replaced) { writeFileSync(filename, replaced, 'utf-8'); @@ -731,7 +741,7 @@ function updateStyles(filename: string) { //if (exists('@capacitor/splash-screen')) { replaced = replaced.replace( '