Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save leodevbro/533a85739a4862523a146571eb6d860e to your computer and use it in GitHub Desktop.
Save leodevbro/533a85739a4862523a146571eb6d860e to your computer and use it in GitHub Desktop.
VS Code Marketplace API - Get Info Of All Extensions
// 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