public/Find-VSCodeExtension.ps1
|
function Find-VSCodeExtension { <# .SYNOPSIS Searches the Visual Studio Code Marketplace via the public Gallery API. .DESCRIPTION Queries the VS Code Marketplace extension gallery and returns matching extensions with key metadata such as publisher, name, version, download count, and VSIX download URL. Supports two modes of operation: - Fuzzy mode (-Query): performs a text search across the marketplace, returning up to -PageSize results ranked by the chosen sort order. - Exact mode (-ExtensionId): performs a precise lookup by qualified extension name (publisher.extensionname), returning only that extension. Optionally, -Version can be used to resolve a specific version; if the requested version is not found, the latest version is returned with a warning. .PARAMETER Query The search term(s) to look for in the marketplace. Used for fuzzy/text search. Cannot be combined with -ExtensionId. .PARAMETER ExtensionId The qualified extension name in publisher.extensionname format (e.g. 'eamodio.gitlens'). Performs an exact lookup. Cannot be combined with -Query. .PARAMETER Version The specific version to resolve when using -ExtensionId (e.g. '16.3.0'). If omitted, the latest version is returned. If the requested version does not exist, falls back to latest with a warning. Only valid with -ExtensionId. .PARAMETER PageSize Number of results to return in fuzzy mode. Defaults to 10, max 100. Has no effect in exact mode since only one extension is returned. .PARAMETER SortBy Sort order for fuzzy mode results. Valid values: Relevance - Best match for the search query (default) Downloads - Most downloaded extensions Rating - Highest rated extensions PublishedDate - Newest extensions .OUTPUTS PSCustomObject with the following properties: Publisher, ExtensionId, DisplayName, Version, TargetPlatforms, Downloads, Rating, Description, VsixUrl .EXAMPLE Find-VSCodeExtension -Query "git" Text search returning the top 10 most relevant git-related extensions. .EXAMPLE Find-VSCodeExtension -Query "python" -PageSize 20 -SortBy Downloads Returns the top 20 Python extensions sorted by download count. .EXAMPLE Find-VSCodeExtension -ExtensionId "eamodio.gitlens" Exact lookup for GitLens, returning the latest published version. .EXAMPLE Find-VSCodeExtension -ExtensionId "eamodio.gitlens" -Version "16.3.0" Exact lookup for a specific version of GitLens. .EXAMPLE Find-VSCodeExtension -ExtensionId "eamodio.gitlens" | Save-VSCodeExtension -Destination C:\vsix Downloads the latest GitLens VSIX to C:\vsix. .NOTES API reference: https://github.com/microsoft/vscode/blob/main/src/vs/platform/extensionManagement/common/extensionGalleryService.ts #> [CmdletBinding(DefaultParameterSetName = 'fuzzy', HelpUri = 'https://steviecoaster.github.io/vscodemarketplace/VSCodeMarketplace/Find-VSCodeExtension/')] param( [Parameter(Mandatory, Position = 0, ParameterSetName = 'fuzzy')] [string] $Query, [Parameter(ParameterSetName = 'exact', Mandatory)] [String] $ExtensionId, [Parameter(ParameterSetName = 'exact')] [String] $Version, [Parameter()] [ValidateRange(1, 100)] [int] $PageSize = 10, [Parameter()] [ValidateSet('Relevance', 'Downloads', 'Rating', 'PublishedDate')] [string] $SortBy = 'Relevance' ) # Map friendly sort names to API sort order values $sortMap = @{ Relevance = 0 PublishedDate = 10 Downloads = 4 Rating = 12 } $criteria = switch ($PSCmdlet.ParameterSetName) { 'fuzzy' { @( @{ filterType = 8; value = 'Microsoft.VisualStudio.Code' } # target platform @{ filterType = 10; value = $Query } # search text ) } 'exact' { @( @{ filterType = 8; value = 'Microsoft.VisualStudio.Code' } # target platform @{ filterType = 7; value = $ExtensionId } # exact qualified name (publisher.extensionname) ) } } $uri = 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery' $body = @{ filters = @( @{ criteria = $criteria pageSize = $PageSize pageNumber = 1 sortBy = $sortMap[$SortBy] sortOrder = 0 } ) assetTypes = @() flags = 914 # includes statistics, versions, and install count } | ConvertTo-Json -Depth 10 $headers = @{ 'Content-Type' = 'application/json' 'Accept' = 'application/json;api-version=7.2-preview.1' } $searchTerm = if ($PSCmdlet.ParameterSetName -eq 'exact') { $ExtensionId } else { $Query } Write-Verbose "Querying VS Code Marketplace for: '$searchTerm' (sort: $SortBy, pageSize: $PageSize)" $response = Invoke-RestMethod -Uri $uri -Method Post -Body $body -Headers $headers $extensions = $response.results[0].extensions if (-not $extensions -or $extensions.Count -eq 0) { Write-Warning "No extensions found for: '$searchTerm'" return } $extensions | ForEach-Object { $ext = $_ # Pull download count from statistics array if available $downloads = ($ext.statistics | Where-Object { $_.statisticName -eq 'install' } | Select-Object -ExpandProperty value -ErrorAction SilentlyContinue) $rating = ($ext.statistics | Where-Object { $_.statisticName -eq 'averagerating' } | Select-Object -ExpandProperty value -ErrorAction SilentlyContinue) $publisher = $ext.publisher.publisherName $extensionName = $ext.extensionName # versions[] is sorted by lastUpdated, not version number — sort explicitly to find the true latest $latestVersion = $ext.versions | Select-Object -ExpandProperty version -Unique | Sort-Object { try { [System.Version]$_ } catch { [System.Version]'0.0.0' } } -Descending | Select-Object -First 1 # If a specific version was requested, use it; otherwise use the latest $resolvedVersion = if ($Version -and ($ext.versions.version -contains $Version)) { $Version } elseif ($Version) { Write-Warning "Version '$Version' not found for '$publisher.$extensionName'. Returning latest ($latestVersion)." $latestVersion } else { $latestVersion } $vsixUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/$publisher/vsextensions/$extensionName/$resolvedVersion/vspackage" # Collect all target platforms available for the resolved version # Each platform ships as a separate entry in versions[] with the same version number $targetPlatforms = @( $ext.versions | Where-Object { $_.version -eq $resolvedVersion } | ForEach-Object { if ($_.targetPlatform) { $_.targetPlatform } else { 'universal' } } | Sort-Object -Unique ) [PSCustomObject]@{ Publisher = $publisher ExtensionId = "$publisher.$extensionName" DisplayName = $ext.displayName Version = $resolvedVersion TargetPlatforms = $targetPlatforms Downloads = if ($downloads) { [int]$downloads } else { 0 } Rating = if ($rating) { [math]::Round($rating, 1) } else { 'N/A' } Description = $ext.shortDescription VsixUrl = $vsixUrl } } } |