Last active
April 10, 2023 07:48
-
-
Save leodevbro/533a85739a4862523a146571eb6d860e to your computer and use it in GitHub Desktop.
VS Code Marketplace API - Get Info Of All Extensions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source: | |
// https://github.com/badges/shields/blob/master/services/visual-studio-marketplace/visual-studio-marketplace-base.js | |
// Base Url of VS Code Marketplace API: | |
const baseUrl = `https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery?api-version=3.0-preview.1`; | |
interface IOneExtensionForFlag256 { | |
publisher: { | |
publisherId: string; // "998b010b-e2af-44a5-a6cd-0b5fd3b9b6f8" | |
publisherName: string; // "ms-python" | |
displayName: string; // "Microsoft" | |
flags: string; // "verified" | |
domain: string; // "https://microsoft.com" | |
isDomainVerified: boolean; // true | |
}; | |
extensionId: string; // "f1f59ae4-9318-4f3c-a9b5-81b2eaa5f8a5" | |
extensionName: string; // "python" | |
displayName: string; // "Python" | |
flags: string; // "validated, public" | |
lastUpdated: string; // "2022-12-26T10:20:51.7Z" | |
publishedDate: string; // "2016-01-19T15:03:11.337Z" | |
releaseDate: string; // "2016-01-19T15:03:11.337Z" | |
shortDescription: string; // "IntelliSense (Pylance), Linting, Debugging (multi-threaded, remote), Jupyter Notebooks, code formatting, refactoring, unit tests, and more." | |
statistics: { | |
statisticName: | |
| "install" | |
| "averagerating" | |
| "ratingcount" | |
| "trendingdaily" | |
| "trendingmonthly" | |
| "trendingweekly" | |
| "updateCount" | |
| "weightedRating" | |
| "downloadCount"; | |
value: number; // 73070873 or maybe 3.0431837164933997 | |
}[]; | |
deploymentType: number; // 0 | |
} | |
interface IGenerateOptionsForList { | |
pageIndexOne: number; | |
} | |
interface IGenerateOptionsForOneSpecificExtension { | |
authorPlusName: string; // leodevbro.blockman | |
} | |
interface IGenerateOptions { | |
mode: { | |
list?: IGenerateOptionsForList; | |
oneSpecificExtension?: IGenerateOptionsForOneSpecificExtension; | |
}; | |
} | |
// ============================== | |
console.log("Hello"); | |
const generateOptionsForPagination = (inp: IGenerateOptions) => { | |
const moOneSpecific = inp.mode.oneSpecificExtension; | |
const moList = inp.mode.list; | |
const currentCriteria = []; | |
if (moOneSpecific?.authorPlusName) { | |
currentCriteria.push({ | |
filterType: 7, // ExtensionName = 7 // https://github.com/microsoft/vscode/blob/e9f533a0bddf77de1a97a210c1dda871228f9a88/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L178 | |
value: moOneSpecific.authorPlusName, // "leodevbro.blockman", // yeah, publisher name, then dot, then ext name. | |
}); | |
} else if (moList?.pageIndexOne) { | |
currentCriteria.push({ | |
filterType: 8, // Target = 8 // https://github.com/microsoft/vscode/blob/e9f533a0bddf77de1a97a210c1dda871228f9a88/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L178 | |
value: "Microsoft.VisualStudio.Code", | |
}); | |
} | |
const newOptions = { | |
filters: [ | |
{ | |
criteria: [...currentCriteria], | |
pageSize: 1000, // more than 1000 results 1000. | |
pageNumber: moList?.pageIndexOne || 1, // numbering starts from 1, not 0. | |
}, | |
], | |
flags: 256, // IncludeStatistics = 0x100 this Hex is 256 as decimal | |
// to get only stats, otherwise the response may boo too large | |
// https://github.com/microsoft/vscode/blob/e9f533a0bddf77de1a97a210c1dda871228f9a88/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L94 | |
// hex-to-decimal converter online: https://www.rapidtables.com/convert/number/hex-to-decimal.html | |
}; | |
return newOptions; | |
}; | |
const getInfosOfExtensions_OnePage = async (pageIndexOne: number) => { | |
const abahen = generateOptionsForPagination({ | |
mode: { list: { pageIndexOne } }, | |
}); | |
let content = undefined; | |
let myExtensions: undefined | IOneExtensionForFlag256[] = undefined; | |
try { | |
const rawResponse = await fetch(baseUrl, { | |
method: "POST", | |
headers: { | |
Accept: "application/json", | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify(abahen), | |
}); | |
content = await rawResponse.json(); | |
} catch (err) { | |
console.log("error during fetching"); | |
throw new Error(err as any); | |
} | |
try { | |
myExtensions = content.results[0].extensions as IOneExtensionForFlag256[]; | |
} catch (err) { | |
console.log("error during reading: content.results[0].extensions"); | |
throw new Error(err as any); | |
} | |
if (myExtensions) { | |
// console.log("done:", myExtensions.length); | |
return myExtensions; | |
} else { | |
return []; | |
} | |
}; | |
const getAllExtensions = async (): Promise<{ | |
storedDate: Date; | |
arr: IOneExtensionForFlag256[]; | |
}> => { | |
const storedDate = new Date(); | |
console.log(storedDate); | |
const theArr: IOneExtensionForFlag256[] = []; | |
let currPageLength = -1; | |
let pageIncr = 1; | |
while (currPageLength !== 0) { | |
const currPageExtensions = await getInfosOfExtensions_OnePage(pageIncr); | |
pageIncr += 1; | |
currPageLength = currPageExtensions.length; | |
theArr.push(...currPageExtensions); | |
console.log("done:", currPageLength, theArr.length); | |
} | |
return { arr: theArr, storedDate }; | |
}; | |
// not used yet | |
const getInfoOfOneSpecificExtension = async (authorPlusName: string) => { | |
const abahen = generateOptionsForPagination({ | |
mode: { oneSpecificExtension: { authorPlusName } }, | |
}); | |
let content = undefined; | |
let myExtensions: undefined | IOneExtensionForFlag256[] = undefined; | |
try { | |
const rawResponse = await fetch(baseUrl, { | |
method: "POST", | |
headers: { | |
Accept: "application/json", | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify(abahen), | |
}); | |
content = await rawResponse.json(); | |
} catch (err) { | |
console.log("error during fetching"); | |
throw new Error(err as any); | |
} | |
try { | |
myExtensions = content.results[0].extensions as IOneExtensionForFlag256[]; | |
} catch (err) { | |
console.log("error during reading: content.results[0].extensions"); | |
throw new Error(err as any); | |
} | |
if (myExtensions) { | |
// console.log("done:", myExtensions.length); | |
return myExtensions; | |
} else { | |
return []; | |
} | |
}; | |
getAllExtensions().then(({ arr: allExtensions, storedDate }) => { | |
console.log("All extensions count:", allExtensions.length); | |
// console.log("First item:"); | |
// console.log(allExtensions[0]); | |
let instNotFoundCount = 0; | |
const superM = allExtensions.map((oneExt) => { | |
const installsObj = oneExt?.statistics?.find( | |
(p) => p.statisticName === "install" | |
); | |
if (!installsObj) { | |
instNotFoundCount += 1; | |
} | |
const installs = installsObj?.value || -1; | |
if (oneExt.extensionName === "blockman") { | |
console.log( | |
"--------------------------------", | |
oneExt.extensionName, | |
installs | |
); | |
} | |
return { | |
extensionName: oneExt.extensionName, | |
installs: installs, | |
}; | |
}); | |
console.log(`installs stat not found for ${instNotFoundCount} extensions.`); | |
const withMinInstalls_1K = superM.filter((q) => { | |
return q.installs >= 1000; | |
}); | |
const withMinInstalls_10K = withMinInstalls_1K.filter((q) => { | |
return q.installs >= 10000; | |
}); | |
const withMinInstalls_100K = withMinInstalls_10K.filter((q) => { | |
return q.installs >= 100000; | |
}); | |
const withMinInstalls_1M = withMinInstalls_100K.filter((q) => { | |
return q.installs >= 1000000; | |
}); | |
const withMinInstalls_10M = withMinInstalls_1M.filter((q) => { | |
return q.installs >= 10000000; | |
}); | |
const withMinInstalls_50M = withMinInstalls_10M.filter((q) => { | |
return q.installs >= 50000000; | |
}); | |
const withMinInstalls_70M = withMinInstalls_50M.filter((q) => { | |
return q.installs >= 70000000; | |
}); | |
const withMinInstalls_80M = withMinInstalls_70M.filter((q) => { | |
return q.installs >= 80000000; | |
}); | |
console.log( | |
"Count of extensions with 1K or more installs:", | |
withMinInstalls_1K.length | |
); | |
console.log( | |
"Count of extensions with 10K or more installs:", | |
withMinInstalls_10K.length | |
); | |
console.log( | |
"Count of extensions with 100K or more installs:", | |
withMinInstalls_100K.length | |
); | |
console.log( | |
"Count of extensions with 1M or more installs:", | |
withMinInstalls_1M.length | |
); | |
console.log( | |
"Count of extensions with 10M or more installs:", | |
withMinInstalls_10M.length | |
); | |
console.log( | |
"Count of extensions with 50M or more installs:", | |
withMinInstalls_50M.length, | |
withMinInstalls_50M.length ? JSON.stringify(withMinInstalls_50M) : null | |
); | |
console.log( | |
"Count of extensions with 70M or more installs:", | |
withMinInstalls_70M.length, | |
withMinInstalls_70M.length ? JSON.stringify(withMinInstalls_70M) : null | |
); | |
console.log( | |
"Count of extensions with 80M or more installs:", | |
withMinInstalls_80M.length, | |
withMinInstalls_80M.length ? JSON.stringify(withMinInstalls_80M) : null | |
); | |
// My personal (leodevbro) interest (VS Code extension "Blockman"): | |
const blockmanRankByInstalls = | |
1 + | |
superM | |
.sort((a, b) => b.installs - a.installs) | |
.findIndex((g) => g.extensionName === "blockman"); | |
console.log("blockmanRankByInstalls:", blockmanRankByInstalls); | |
console.log(storedDate); | |
}); | |
// - |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment