Skip to content

Commit

Permalink
1.62 (#98)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dtarnawsky authored Dec 6, 2023
1 parent 1d92673 commit cb98244
Show file tree
Hide file tree
Showing 27 changed files with 538 additions and 314 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
13 changes: 9 additions & 4 deletions roadmap.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions src/analyze-size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.');
Expand Down
13 changes: 7 additions & 6 deletions src/android-debug-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Recommendation[]> {
if (ionicState.refreshDebugDevices) {
ionicState.refreshDebugDevices = false;
Expand All @@ -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);
}
Expand All @@ -43,7 +43,7 @@ export async function getAndroidWebViewList(
device.product,
TreeItemCollapsibleState.None,
getCommand(),
undefined
undefined,
);
r.setIcon('android');
result.push(r);
Expand All @@ -52,7 +52,8 @@ export async function getAndroidWebViewList(
return result;
}

async function debug(device: Device, webview: WebView, wwwfolder: string): Promise<void> {
async function debug(queueFunction: QueueFunction, device: Device, webview: WebView, wwwfolder: string): Promise<void> {
queueFunction();
debugAndroid(webview.packageName, wwwfolder);
return;
}
Expand Down
8 changes: 7 additions & 1 deletion src/angular-generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
export async function angularGenerate(
queueFunction: QueueFunction,
project: Project,
angularType: string,
): Promise<void> {
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 {
Expand Down
92 changes: 88 additions & 4 deletions src/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
export async function audit(queueFunction: QueueFunction, project: Project): Promise<void> {
try {
queueFunction();
clearOutput();
let vulnerabilities: SecurityVulnerability[] = [];
await showProgress('Auditing project dependencies...', async () => {
let folder = project.projectFolder();
if (project.monoRepo?.nodeModulesAtRoot) {
Expand All @@ -14,7 +18,15 @@ export async function audit(project: Project): Promise<void> {
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);
Expand All @@ -26,11 +38,68 @@ export async function audit(project: Project): Promise<void> {
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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down
16 changes: 9 additions & 7 deletions src/capacitor-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 };
Expand All @@ -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);
Expand All @@ -78,7 +80,7 @@ async function openPortal(platform: CapacitorPlatform) {
async function verifySettings(
project: Project,
platform: CapacitorPlatform,
settings: KeyStoreSettings
settings: KeyStoreSettings,
): Promise<KeyStoreSettings> {
if (platform == CapacitorPlatform.ios) return settings;

Expand All @@ -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;
Expand Down Expand Up @@ -146,7 +148,7 @@ function capBuildCommand(
project: Project,
platform: CapacitorPlatform,
args: string,
settings: KeyStoreSettings
settings: KeyStoreSettings,
): string {
switch (project.repoType) {
case MonoRepoType.none:
Expand All @@ -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}"`;
Expand Down Expand Up @@ -229,7 +231,7 @@ function writeConfig(project: Project, settings: KeyStoreSettings) {
keystoreAlias: '',
}
}
};`
};`,
);
}
writeFileSync(filename, data);
Expand Down
Loading

0 comments on commit cb98244

Please sign in to comment.