From 2da613faf63631f568e487f33ec0d88c263889d5 Mon Sep 17 00:00:00 2001 From: chid Date: Wed, 15 Apr 2026 13:28:52 +1000 Subject: [PATCH] Improve unsupported spfx-version error --- .../PublicGitHubRepositorySource.ts | 32 +++++++++++++++++++ .../test/PublicGitHubRepositorySource.test.ts | 18 +++++++++++ .../PublicGitHubRepositorySource.test.ts.snap | 2 ++ ...ersion-error-message_2026-04-15-13-28.json | 10 ++++++ 4 files changed, 62 insertions(+) create mode 100644 common/changes/@microsoft/spfx-cli/spfx-version-error-message_2026-04-15-13-28.json diff --git a/api/spfx-template-api/src/repositories/PublicGitHubRepositorySource.ts b/api/spfx-template-api/src/repositories/PublicGitHubRepositorySource.ts index 9ed227e7..5efd5288 100644 --- a/api/spfx-template-api/src/repositories/PublicGitHubRepositorySource.ts +++ b/api/spfx-template-api/src/repositories/PublicGitHubRepositorySource.ts @@ -122,6 +122,10 @@ export interface IPublicGitHubRepositorySourceOptions { // Matches https:////[.git] — HTTPS only, host-agnostic for GHE support. const REPO_URL_REGEX: RegExp = /^https:\/\/([^/]+)\/([^/]+)\/([^/]+?)(\.git)?$/; +const OFFICIAL_SPFX_REPO_HOST: string = 'github.com'; +const OFFICIAL_SPFX_REPO_OWNER: string = 'sharepoint'; +const OFFICIAL_SPFX_REPO_NAME: string = 'spfx'; +const OFFICIAL_SPFX_AVAILABLE_VERSIONS_MESSAGE: string = 'latest (1.22), next (1.23.0-beta)'; /** * @public @@ -208,6 +212,13 @@ export class PublicGitHubRepositorySource extends BaseSPFxTemplateRepositorySour } const response: Response = await fetch(downloadUrl, fetchInit); if (!response.ok) { + const invalidSpfxVersionErrorMessage: string | undefined = this._getInvalidSpfxVersionErrorMessage( + response.status + ); + if (invalidSpfxVersionErrorMessage) { + throw new Error(invalidSpfxVersionErrorMessage); + } + throw new Error(`Failed to download repository: ${response.status} ${response.statusText}`); } @@ -215,6 +226,27 @@ export class PublicGitHubRepositorySource extends BaseSPFxTemplateRepositorySour return this._extractZipBuffer(zipBuffer); } + private _getInvalidSpfxVersionErrorMessage(status: number): string | undefined { + if (status !== 404 || !this._ref.startsWith('version/')) { + return undefined; + } + + const { host, owner, repo } = this._parseRepoUrl(); + if ( + host !== OFFICIAL_SPFX_REPO_HOST || + owner.toLowerCase() !== OFFICIAL_SPFX_REPO_OWNER || + repo.toLowerCase() !== OFFICIAL_SPFX_REPO_NAME + ) { + return undefined; + } + + const requestedVersion: string = this._ref.slice('version/'.length); + return ( + `Unsupported value for --spfx-version "${requestedVersion}". ` + + `Available versions: ${OFFICIAL_SPFX_AVAILABLE_VERSIONS_MESSAGE}.` + ); + } + private _extractZipBuffer(zipBuffer: Buffer): Map { const fileMap: Map = new Map(); const zip: AdmZip = new AdmZip(zipBuffer); diff --git a/api/spfx-template-api/src/repositories/test/PublicGitHubRepositorySource.test.ts b/api/spfx-template-api/src/repositories/test/PublicGitHubRepositorySource.test.ts index 549c20d1..4e43230f 100644 --- a/api/spfx-template-api/src/repositories/test/PublicGitHubRepositorySource.test.ts +++ b/api/spfx-template-api/src/repositories/test/PublicGitHubRepositorySource.test.ts @@ -641,6 +641,24 @@ describe(PublicGitHubRepositorySource.name, () => { ); }); + it('should list available versions when --spfx-version does not exist in the official repo', async () => { + mockFetch.mockResolvedValue({ + ok: false, + status: 404, + statusText: 'Not Found' + } as unknown as Response); + + const source = new PublicGitHubRepositorySource({ + repoUrl: 'https://github.com/SharePoint/spfx', + branch: 'version/1.21', + terminal + }); + + await expect(source.getTemplatesAsync()).rejects.toThrow( + /Unsupported value for --spfx-version "1.21"\. Available versions: latest \(1\.22\), next \(1\.23\.0-beta\)\./ + ); + }); + it('should throw error when network request fails', async () => { mockFetch.mockRejectedValue(new Error('Network error')); diff --git a/api/spfx-template-api/src/repositories/test/__snapshots__/PublicGitHubRepositorySource.test.ts.snap b/api/spfx-template-api/src/repositories/test/__snapshots__/PublicGitHubRepositorySource.test.ts.snap index ed8b9a0a..c859bcf9 100644 --- a/api/spfx-template-api/src/repositories/test/__snapshots__/PublicGitHubRepositorySource.test.ts.snap +++ b/api/spfx-template-api/src/repositories/test/__snapshots__/PublicGitHubRepositorySource.test.ts.snap @@ -74,6 +74,8 @@ exports[`PublicGitHubRepositorySource getTemplates should send Authorization hea exports[`PublicGitHubRepositorySource getTemplates should throw error when download fails 1`] = `Array []`; +exports[`PublicGitHubRepositorySource getTemplates should list available versions when --spfx-version does not exist in the official repo 1`] = `Array []`; + exports[`PublicGitHubRepositorySource getTemplates should throw error when network request fails 1`] = `Array []`; exports[`PublicGitHubRepositorySource getTemplates should use GHE API URL for enterprise hosts 1`] = `Array []`; diff --git a/common/changes/@microsoft/spfx-cli/spfx-version-error-message_2026-04-15-13-28.json b/common/changes/@microsoft/spfx-cli/spfx-version-error-message_2026-04-15-13-28.json new file mode 100644 index 00000000..f79f5fc7 --- /dev/null +++ b/common/changes/@microsoft/spfx-cli/spfx-version-error-message_2026-04-15-13-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/spfx-cli", + "comment": "Improve the unsupported --spfx-version error to list the currently available versions.", + "type": "none" + } + ], + "packageName": "@microsoft/spfx-cli" +}