Module

x/udd/registry.ts

Update Deno Dependencies - update dependency urls to their latest published versions
Very Popular
Go to Latest
File
export type RegistryCtor = new (url: string) => RegistryUrl;export function lookup(url: string, registries: RegistryCtor[]): | RegistryUrl | undefined { for (const R of registries) { const u = new R(url); if (u.regexp.test(url)) { return u; } }}
export interface RegistryUrl { url: string; // all versions of the url all: () => Promise<string[]>; // url at a given version at(version: string): RegistryUrl; // current version of url version: () => string; // is url valid for this RegistryUrl regexp: RegExp;}
export function defaultAt(that: RegistryUrl, version: string): string { return that.url.replace(/@(.*?)(\/|$)/, `@${version}/`);}
export function defaultVersion(that: RegistryUrl): string { const v = that.url.match(/\@([^\/]+)[\/$]?/); if (v === null) { throw Error(`Unable to find version in ${that.url}`); } return v[1];}
export function defaultName(that: RegistryUrl): string { const n = that.url.match(/([^\/\"\']*?)\@[^\'\"]*/); if (n === null) { throw new Error(`Package name not found in ${that.url}`); } return n[1];}
async function githubDownloadReleases( owner: string, repo: string, lastVersion: string | undefined = undefined,): Promise<string[]> { let url = `https://github.com/${owner}/${repo}/releases.atom`; if (lastVersion) { url += `?after=${lastVersion}`; } // FIXME do we need to handle 404?
const page = await fetch(url); const text = await page.text(); return [ ...text.matchAll( /\<id\>tag\:github\.com\,2008\:Repository\/\d+\/(.*?)\<\/id\>/g, ), ].map((x) => x[1]);}
// export for testing purposes// FIXME this should really be lazy, we shouldn't always iterate everything...export const GR_CACHE: Map<string, string[]> = new Map<string, string[]>();async function githubReleases( owner: string, repo: string, cache: Map<string, string[]> = GR_CACHE,): Promise<string[]> { const cacheKey = `${owner}/${repo}`; if (cache.has(cacheKey)) { return cache.get(cacheKey)!; } const versions = await githubDownloadReleases(owner, repo); if (versions.length === 10) { let lastVersion: string | undefined = undefined; // arbitrarily we're going to limit to 5 pages...? let i: number = 0; while (lastVersion !== versions[versions.length - 1] && i < 5) { i++; lastVersion = versions[versions.length - 1]; versions.push(...await githubDownloadReleases(owner, repo, lastVersion)); } } cache.set(cacheKey, versions); return versions;}
interface DenoLandDBEntry { type: string; owner: string; repo: string;}let denoLandDB: Record<string, DenoLandDBEntry>;
export class DenoLand implements RegistryUrl { url: string;
constructor(url: string) { this.url = url; }
name(): string { return defaultName(this); }
async all(): Promise<string[]> { if (!denoLandDB) { const dbUrl = "https://raw.githubusercontent.com/denoland/deno_website2/master/database.json"; denoLandDB = await (await fetch(dbUrl)).json(); } let res: DenoLandDBEntry; try { res = denoLandDB[this.name()]; } catch { throw new Error(`${this.name()} not found in deno land json`); }
const { type, owner, repo } = res; if (type !== "github") { throw new Error(`${this.name()} has unsupported type ${type}`); }
return await githubReleases(owner, repo); }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new DenoLand(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/deno.land\/x\/[^\/\"\']*?\@[^\'\"]*/;}
export class DenoStd implements RegistryUrl { url: string;
constructor(url: string) { this.url = url; }
async all(): Promise<string[]> { const denoReleases = await githubReleases("denoland", "deno"); return denoReleases .filter((x) => x.startsWith("std/")) .map((x) => "v" + x.split("/")[1]) }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new DenoStd(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/deno.land\/std\@[^\'\"]*/;}
async function unpkgVersions(name: string): Promise<string[]> { const page = await fetch(`https://unpkg.com/browse/${name}/`); const text = await page.text(); // naively, we grab all the options const m = [...text.matchAll(/\<option[^\<\>]* value\=\"(.*?)\"\>/g)]; m.reverse(); return m.map((x) => x[1]);}
export class Unpkg implements RegistryUrl { url: string;
name(): string { return defaultName(this); }
constructor(url: string) { this.url = url; }
async all(): Promise<string[]> { return await unpkgVersions(this.name()); }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new Unpkg(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/unpkg.com\/[^\/\"\']*?\@[^\'\"]*/;}
export class Jspm implements RegistryUrl { url: string;
name(): string { return defaultName(this); }
constructor(url: string) { this.url = url; }
async all(): Promise<string[]> { return await unpkgVersions(this.name()); }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new Jspm(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/dev.jspm.io\/[^\/\"\']*?\@[^\'\"]*/;}
export class Denopkg implements RegistryUrl { url: string;
owner(): string { return this.url.split("/")[3]; }
repo(): string { return defaultName(this); }
constructor(url: string) { this.url = url; }
async all(): Promise<string[]> { return await githubReleases(this.owner(), this.repo()); }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new Denopkg(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/denopkg.com\/[^\/\"\']*?\/[^\/\"\']*?\@[^\'\"]*/;}
// TODO Pika
export class Pika implements RegistryUrl { url: string;
name(): string { return defaultName(this); }
constructor(url: string) { this.url = url; }
async all(): Promise<string[]> { return await unpkgVersions(this.name()); }
at(version: string): RegistryUrl { const url = defaultAt(this, version); return new Pika(url); }
version(): string { return defaultVersion(this); }
regexp: RegExp = /https?:\/\/cdn.pika.dev(\/\_)?\/[^\/\"\']*?\@[^\'\"]*/;}
export class GithubRaw implements RegistryUrl { url: string;
constructor(url: string) { this.url = url; }
all(): Promise<string[]> { const [,,, user, repo] = this.url.split("/") ; return githubReleases(user, repo); }
at(version: string): RegistryUrl { const parts = this.url.split("/"); parts[5] = version; return new GithubRaw(parts.join("/")); }
version(): string { const v = this.url.split("/")[5]; if (v === undefined) { throw Error(`Unable to find version in ${this.url}`); } return v; }
regexp: RegExp = /https?:\/\/raw\.githubusercontent\.com\/[^\/\"\']+\/[^\/\"\']+\/(?!master)[^\/\"\']+\/[^\'\"]*/;}
async function gitlabDownloadReleases( owner: string, repo: string, page: number,): Promise<string[]> { const url = `https://gitlab.com/${owner}/${repo}/-/tags?format=atom&page=${page}`
const text = await (await fetch(url)).text(); return [ ...text.matchAll( /\<id\>https\:\/\/gitlab.com.+\/-\/tags\/(.+?)\<\/id\>/g, ), ].map((x) => x[1]);}
// export for testing purposes// FIXME this should really be lazy, we shouldn't always iterate everything...export const GL_CACHE: Map<string, string[]> = new Map<string, string[]>();async function gitlabReleases( owner: string, repo: string, cache: Map<string, string[]> = GL_CACHE,): Promise<string[]> { const cacheKey = `${owner}/${repo}`; if (cache.has(cacheKey)) { return cache.get(cacheKey)!; } // to roughly match GitHub above (5 pages, 10 releases each), we'll // limit to 3 pages, 20 releases each let i = 1; const versions = await gitlabDownloadReleases(owner, repo, i); if (versions.length === 20) { let lastVersion: string | undefined = undefined; while (lastVersion !== versions[versions.length - 1] && i <= 3) { i++; lastVersion = versions[versions.length - 1]; versions.push(...await gitlabDownloadReleases(owner, repo, i)); } } cache.set(cacheKey, versions); return versions;}
export class GitlabRaw implements RegistryUrl { url: string;
constructor(url: string) { this.url = url; }
all(): Promise<string[]> { const [,,, user, repo] = this.url.split("/") ; return gitlabReleases(user, repo); }
at(version: string): RegistryUrl { const parts = this.url.split("/"); parts[7] = version; return new GithubRaw(parts.join("/")); }
version(): string { const v = this.url.split("/")[7]; if (v === undefined) { throw Error(`Unable to find version in ${this.url}`); } return v; }
regexp: RegExp = /https?:\/\/gitlab\.com\/[^\/\"\']+\/[^\/\"\']+\/-\/raw\/(?!master)[^\/\"\']+\/[^\'\"]*/;}
export const REGISTRIES = [DenoStd, DenoLand, Unpkg, Denopkg, Jspm, Pika, GithubRaw, GitlabRaw];