From 68bc05af58b8256b4c2c404f0af3b6d395cbc781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= <58668583+Adam-it@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:13:19 +0200 Subject: [PATCH 1/5] Removes yarn package manager. Closes #834 (#853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎯 Aim The aim is to drop yarn package manager support ## 📷 Result image ## ✅ What was done - [X] Removed yarn from settings and all places where it was handled specifically ## 🔗 Related issue Closes #834 --------- Co-authored-by: Saurabh Tripathi --- package.json | 5 ++--- src/constants/PackageManagers.ts | 3 +-- src/services/actions/Dependencies.ts | 10 ++-------- src/test/suite/packageManager.test.ts | 3 +-- src/utils/getInstallCommand.ts | 4 ++-- src/utils/getPackageManager.ts | 2 +- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index cd69ba6f..2756a416 100644 --- a/package.json +++ b/package.json @@ -833,10 +833,9 @@ "default": "npm", "enum": [ "npm", - "pnpm", - "yarn" + "pnpm" ], - "description": "Choose your preferred Node package manager. Choose between `npm`, `pnpm`, or `yarn`." + "description": "Choose your preferred Node package manager. Choose between `npm` and `pnpm`." }, "spfx-toolkit.showServiceIncidentList": { "title": "Show service health incidents", diff --git a/src/constants/PackageManagers.ts b/src/constants/PackageManagers.ts index 66ddb285..1c06e718 100644 --- a/src/constants/PackageManagers.ts +++ b/src/constants/PackageManagers.ts @@ -1,6 +1,5 @@ // eslint-disable-next-line no-shadow export enum PackageManagers { npm = 'npm', - pnpm = 'pnpm', - yarn = 'yarn' + pnpm = 'pnpm' } \ No newline at end of file diff --git a/src/services/actions/Dependencies.ts b/src/services/actions/Dependencies.ts index 7aab61b3..0c076b81 100644 --- a/src/services/actions/Dependencies.ts +++ b/src/services/actions/Dependencies.ts @@ -87,14 +87,8 @@ export class Dependencies { const packageManager = getPackageManager(); let installCommand: string; - // Handle different global installation syntax for each package manager - if (packageManager === PackageManagers.yarn) { - installCommand = `${packageManager} global add ${dependencies} @microsoft/generator-sharepoint@${spfxVersion.Version}`; - } else { - // npm and pnpm both use: -g - const cmd = packageManager === PackageManagers.npm ? 'install' : 'add'; - installCommand = `${packageManager} ${cmd} -g ${dependencies} @microsoft/generator-sharepoint@${spfxVersion.Version}`; - } + const cmd = packageManager === PackageManagers.npm ? 'install' : 'add'; + installCommand = `${packageManager} ${cmd} -g ${dependencies} @microsoft/generator-sharepoint@${spfxVersion.Version}`; await TerminalCommandExecuter.runCommandAndWait(installCommand, 'Installing dependencies', 'cloud-download'); diff --git a/src/test/suite/packageManager.test.ts b/src/test/suite/packageManager.test.ts index 33728cef..f1a06ec9 100644 --- a/src/test/suite/packageManager.test.ts +++ b/src/test/suite/packageManager.test.ts @@ -25,7 +25,7 @@ suite('Package Manager Settings', () => { const packageManagerSetting = contributedSettings['spfx-toolkit.packageManager']; assert(packageManagerSetting, 'Package Manager setting is not contributed'); - assert.deepStrictEqual(packageManagerSetting.enum, ['npm', 'pnpm', 'yarn']); + assert.deepStrictEqual(packageManagerSetting.enum, ['npm', 'pnpm']); }); test('should verify that package manager setting can be read', async () => { @@ -39,6 +39,5 @@ suite('Package Manager Settings', () => { test('should verify that PackageManagers enum has correct values', () => { assert.strictEqual(PackageManagers.npm, 'npm'); assert.strictEqual(PackageManagers.pnpm, 'pnpm'); - assert.strictEqual(PackageManagers.yarn, 'yarn'); }); }); diff --git a/src/utils/getInstallCommand.ts b/src/utils/getInstallCommand.ts index e5418a4a..73280a14 100644 --- a/src/utils/getInstallCommand.ts +++ b/src/utils/getInstallCommand.ts @@ -3,9 +3,9 @@ import { PackageManagers } from '../constants'; /** * Helper function to get the appropriate command for adding specific packages. * When installing all dependencies (no package names), all package managers use 'install'. - * When adding specific packages, npm uses 'install' while pnpm and yarn use 'add'. + * When adding specific packages, npm uses 'install' while pnpm use 'add'. * @param packageManager - The package manager to use - * @returns The command for adding specific packages ('install' for npm, 'add' for pnpm/yarn) + * @returns The command for adding specific packages ('install' for npm, 'add' for pnpm) */ export const getInstallCommand = (packageManager: PackageManagers): string => { return packageManager === PackageManagers.npm ? 'install' : 'add'; diff --git a/src/utils/getPackageManager.ts b/src/utils/getPackageManager.ts index f86efee1..0e16ada8 100644 --- a/src/utils/getPackageManager.ts +++ b/src/utils/getPackageManager.ts @@ -4,7 +4,7 @@ import { PackageManagers } from '../constants'; /** * Retrieves the package manager setting from VS Code extension settings. * If the setting is not found, defaults to 'npm'. - * @returns The package manager to use (npm, pnpm, or yarn) + * @returns The package manager to use (npm, pnpm) */ export const getPackageManager = (): PackageManagers => { return getExtensionSettings('packageManager', PackageManagers.npm); From fa6f003126a050b8bc1dbc0e0a85dabc3e39463c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= <58668583+Adam-it@users.noreply.github.com> Date: Wed, 12 Aug 2026 14:55:29 +0200 Subject: [PATCH 2/5] Adds export of site level app catalogs with apps. Closes #573 (#855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎯 Aim The aim is to add an export button that will export site level app catalogs with apps to a JSON report ## 📷 Result image image image ## ✅ What was done - [X] Adds new export command for the site level app catalogs tree node ## 🔗 Related issue Closes #573 --- package.json | 13 +++- src/constants/Commands.ts | 1 + src/models/SiteAppCatalogExport.ts | 5 ++ src/models/index.ts | 1 + src/services/actions/CliActions.ts | 112 ++++++++++++++++++++++++++--- 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 src/models/SiteAppCatalogExport.ts diff --git a/package.json b/package.json index 2756a416..18bd1a3a 100644 --- a/package.json +++ b/package.json @@ -1296,6 +1296,12 @@ "category": "SPFx Toolkit", "icon": "$(trash)" }, + { + "command": "spfx-toolkit.exportSiteAppCatalogs", + "title": "Export Site App Catalogs", + "category": "SPFx Toolkit", + "icon": "$(export)" + }, { "command": "spfx-toolkit.moreTenantWideExtensionActions", "title": "...", @@ -1450,7 +1456,12 @@ { "command": "spfx-toolkit.addSiteAppCatalog", "when": "viewItem == sp-app-catalog-root", - "group": "inline" + "group": "inline@1" + }, + { + "command": "spfx-toolkit.exportSiteAppCatalogs", + "when": "viewItem == sp-app-catalog-root", + "group": "inline@2" }, { "command": "spfx-toolkit.removeSiteAppCatalog", diff --git a/src/constants/Commands.ts b/src/constants/Commands.ts index b8f73f67..bdb2421c 100644 --- a/src/constants/Commands.ts +++ b/src/constants/Commands.ts @@ -101,6 +101,7 @@ export const Commands = { addTenantAppCatalog: `${EXTENSION_NAME}.addTenantAppCatalog`, addSiteAppCatalog: `${EXTENSION_NAME}.addSiteAppCatalog`, removeSiteAppCatalog: `${EXTENSION_NAME}.removeSiteAppCatalog`, + exportSiteAppCatalogs: `${EXTENSION_NAME}.exportSiteAppCatalogs`, // Set form customizer setFormCustomizer: `${EXTENSION_NAME}.setFormCustomizer`, diff --git a/src/models/SiteAppCatalogExport.ts b/src/models/SiteAppCatalogExport.ts new file mode 100644 index 00000000..92847042 --- /dev/null +++ b/src/models/SiteAppCatalogExport.ts @@ -0,0 +1,5 @@ +export interface SiteAppCatalogExport { + url: string; + apps: Record[]; + error?: string; +} diff --git a/src/models/index.ts b/src/models/index.ts index 1434317e..b00875a4 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -5,6 +5,7 @@ export * from './GenerateWorkflowCommandInput'; export * from './Sample'; export * from './ServeConfig'; export * from './SiteAppCatalog'; +export * from './SiteAppCatalogExport'; export * from './solution-add-result'; export * from './SpfxAddComponentCommandInput'; export * from './SpfxDoctorOutput'; diff --git a/src/services/actions/CliActions.ts b/src/services/actions/CliActions.ts index 6ca4a159..761d8774 100644 --- a/src/services/actions/CliActions.ts +++ b/src/services/actions/CliActions.ts @@ -1,8 +1,9 @@ import { readFileSync, writeFileSync } from 'fs'; +import { homedir } from 'os'; import { Folders } from '../check/Folders'; import { commands, Progress, ProgressLocation, Uri, window, workspace, WorkspaceFolder } from 'vscode'; import { Commands, SpfxCompatibilityMatrix, WebViewType, WebviewCommand, WorkflowType } from '../../constants'; -import { AppCatalogApp, GenerateWorkflowCommandInput, SiteAppCatalog, SolutionAddResult, SpfxDoctorOutput, Subscription } from '../../models'; +import { AppCatalogApp, GenerateWorkflowCommandInput, SiteAppCatalog, SiteAppCatalogExport, SolutionAddResult, SpfxDoctorOutput, Subscription } from '../../models'; import { Extension } from '../dataType/Extension'; import { CliExecuter } from '../executeWrappers/CliCommandExecuter'; import { Notifications } from '../dataType/Notifications'; @@ -59,6 +60,9 @@ export class CliActions { subscriptions.push( commands.registerCommand(Commands.removeSiteAppCatalog, CliActions.removeSiteAppCatalog) ); + subscriptions.push( + commands.registerCommand(Commands.exportSiteAppCatalogs, CliActions.exportSiteAppCatalogs) + ); } /** @@ -175,15 +179,8 @@ export class CliActions { */ public static async getAppCatalogApps(appCatalogUrl?: string): Promise { try { - const commandOptions: any = appCatalogUrl && appCatalogUrl.trim() !== '' ? { - appCatalogScope: 'sitecollection', - appCatalogUrl: appCatalogUrl - } : {}; - - const response = (await CliExecuter.execute('spo app list', 'json', commandOptions)); - const apps = response?.stdout || '[]'; + const appsJson = await CliActions.getAppCatalogAppsRaw(appCatalogUrl); - const appsJson: any[] = JSON.parse(apps); const appList = appsJson.map(({ ID, Title, Deployed, IsEnabled }) => { return { ID, @@ -200,6 +197,23 @@ export class CliActions { } } + /** + * Retrieves the unmapped 'spo app list' output for the tenant or site app catalog. + * + * @param appCatalogUrl The URL of the tenant or site app catalog. + * @returns A promise that resolves to the app objects as returned by the CLI, with all their properties. + */ + public static async getAppCatalogAppsRaw(appCatalogUrl?: string): Promise { + const commandOptions: any = appCatalogUrl && appCatalogUrl.trim() !== '' ? { + appCatalogScope: 'sitecollection', + appCatalogUrl: appCatalogUrl + } : {}; + + const response = (await CliExecuter.execute('spo app list', 'json', commandOptions)); + + return JSON.parse(response?.stdout || '[]'); + } + /** * Retrieves the tenant-wide extensions from the specified tenant app catalog URL. * @param tenantAppCatalogUrl The URL of the tenant app catalog. @@ -671,6 +685,86 @@ export class CliActions { } } + /** + * Exports an inventory of all site collection app catalogs and the apps they contain. + */ + public static async exportSiteAppCatalogs() { + try { + const appCatalogUrls = await CliActions.appCatalogUrlsGet(); + // the first entry is the tenant app catalog, which is out of scope for this export + const siteAppCatalogUrls = appCatalogUrls?.slice(1) ?? []; + + if (siteAppCatalogUrls.length === 0) { + Notifications.warning('No site app catalogs found to export.'); + return; + } + + const siteAppCatalogs = await window.withProgress({ + location: ProgressLocation.Notification, + title: `Collecting the site app catalog details... Check [output window](command:${Commands.showOutputChannel}) to follow the progress.`, + cancellable: false + }, async () => { + const catalogs: SiteAppCatalogExport[] = []; + + for (const siteAppCatalogUrl of siteAppCatalogUrls) { + try { + catalogs.push({ + url: siteAppCatalogUrl, + apps: await CliActions.getAppCatalogAppsRaw(siteAppCatalogUrl) + }); + } catch (e: any) { + catalogs.push({ + url: siteAppCatalogUrl, + apps: [], + error: e?.error?.message || e?.message || 'Failed to retrieve the apps of this site app catalog.' + }); + } + } + + return catalogs; + }); + + const timestamp = new Date().toISOString().slice(0, 16).replace(/[:T]/g, '-'); + const defaultPath = join(workspace.workspaceFolders?.[0]?.uri.fsPath || homedir(), `site-app-catalogs-${timestamp}.json`); + + const targetUri = await window.showSaveDialog({ + defaultUri: Uri.file(defaultPath), + filters: { JSON: ['json'] }, + saveLabel: 'Export' + }); + + if (!targetUri) { + return; + } + + writeFileSync(targetUri.fsPath, CliActions.buildSiteAppCatalogsExport(siteAppCatalogs), 'utf8'); + + const openFile = 'Open file'; + Notifications.info(`Exported ${siteAppCatalogs.length} site app catalog(s) to '${basename(targetUri.fsPath)}'.`, openFile).then((selectedOption) => { + if (selectedOption === openFile) { + commands.executeCommand('vscode.open', targetUri); + } + }); + } catch (e: any) { + const message = e?.error?.message || e?.message || 'An unexpected error occurred during the export.'; + Notifications.error(message); + } + } + + /** + * Builds the content of the site app catalogs export file. + * + * @param siteAppCatalogs The site app catalogs to include in the export. + * @returns The serialized export content. + */ + private static buildSiteAppCatalogsExport(siteAppCatalogs: SiteAppCatalogExport[]): string { + return JSON.stringify({ + generatedOn: new Date().toISOString(), + tenantUrl: EnvironmentInformation.tenantUrl, + siteAppCatalogs + }, null, 2); + } + /** * Upgrades the project by generating the upgrade steps and displaying them in a Markdown preview. * @private From 498c268199014f2390de567da1ffa3744bcc8230 Mon Sep 17 00:00:00 2001 From: Saurabh Tripathi Date: Thu, 13 Aug 2026 09:49:23 +0200 Subject: [PATCH 3/5] pre-release 4.20.1 --- npm-shrinkwrap.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 671dd7f5..6946bd69 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "viva-connections-toolkit", - "version": "4.20.0", + "version": "4.20.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "viva-connections-toolkit", - "version": "4.20.0", + "version": "4.20.1", "license": "MIT", "dependencies": { "@grconrad/vscode-extension-feedback": "1.0.0", diff --git a/package.json b/package.json index 18bd1a3a..7782b357 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "viva-connections-toolkit", "displayName": "SharePoint Framework Toolkit", "description": "SharePoint Framework Toolkit aims to boost your productivity in developing and managing SharePoint Framework solutions helping at every stage of your development flow, from setting up your development workspace to deploying a solution straight to your tenant without the need to leave VS Code and now even create a CI/CD pipeline to introduce automate deployment of your app. This toolkit is provided by the community.", - "version": "4.20.0", + "version": "4.20.1", "publisher": "m365pnp", "preview": false, "homepage": "https://pnp.github.io/vscode-viva", From ca65b515a222acbe22d3a5b903cc392a15911806 Mon Sep 17 00:00:00 2001 From: Saurabh Tripathi Date: Fri, 14 Aug 2026 21:02:32 +0200 Subject: [PATCH 4/5] update update-package-version.ps1 to bump version in npm-shrinkwrap.json --- scripts/update-package-version.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/update-package-version.ps1 b/scripts/update-package-version.ps1 index 447fc2e6..8d419440 100644 --- a/scripts/update-package-version.ps1 +++ b/scripts/update-package-version.ps1 @@ -8,4 +8,7 @@ $oldVersion = $packageJson.version $oldVersionParts = $oldVersion.Split(".") $newVersion = "$($oldVersionParts[0]).$($oldVersionParts[1]).$([int]$oldVersionParts[2] + 1)" $packageJson.version = $newVersion -$packageJson | ConvertTo-Json -Depth 10 | Out-File "$PathToWorkspace\package.json" -Encoding utf8 -Force \ No newline at end of file +$packageJson | ConvertTo-Json -Depth 10 | Out-File "$PathToWorkspace\package.json" -Encoding utf8 -Force + +$shrinkwrapPath = "$PathToWorkspace\npm-shrinkwrap.json" +(Get-Content $shrinkwrapPath -Raw).Replace("`"$oldVersion`"", "`"$newVersion`"") | Out-File $shrinkwrapPath -Encoding utf8 -Force \ No newline at end of file From bdf7a5e7ded2d783a77c99421ce1431084a7241d Mon Sep 17 00:00:00 2001 From: Saurabh Tripathi Date: Mon, 17 Aug 2026 20:05:23 +0200 Subject: [PATCH 5/5] removed string replace; using JSON update --- scripts/update-package-version.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/update-package-version.ps1 b/scripts/update-package-version.ps1 index 8d419440..440d1914 100644 --- a/scripts/update-package-version.ps1 +++ b/scripts/update-package-version.ps1 @@ -11,4 +11,7 @@ $packageJson.version = $newVersion $packageJson | ConvertTo-Json -Depth 10 | Out-File "$PathToWorkspace\package.json" -Encoding utf8 -Force $shrinkwrapPath = "$PathToWorkspace\npm-shrinkwrap.json" -(Get-Content $shrinkwrapPath -Raw).Replace("`"$oldVersion`"", "`"$newVersion`"") | Out-File $shrinkwrapPath -Encoding utf8 -Force \ No newline at end of file +$shrinkwrap = Get-Content $shrinkwrapPath -Raw | ConvertFrom-Json -AsHashtable +$shrinkwrap['version'] = $newVersion +$shrinkwrap['packages']['']['version'] = $newVersion +$shrinkwrap | ConvertTo-Json -Depth 100 | Out-File $shrinkwrapPath -Encoding utf8 -Force