|
| 1 | +import { writeFileSync } from 'node:fs'; |
| 2 | +import path from 'node:path'; |
| 3 | +import semver from 'semver'; |
| 4 | +import { PACKAGES } from '../src/lib/plugins/packages.js'; |
| 5 | + |
| 6 | +interface PackageInfo { |
| 7 | + readme: string; |
| 8 | + createdAt: string; |
| 9 | + updatedAt: string; |
| 10 | + description: string; |
| 11 | + weeklyNPMDownloads: number; |
| 12 | +} |
| 13 | + |
| 14 | +// FIXME: fetchPackageInfo is a simpler version of `@theguild/components`'s fetchPackageInfo |
| 15 | +// We cannot use the shared package version because it has imports that Next.js understands |
| 16 | +// but not a simple script like this. So, inlining this version helps unblock deployment |
| 17 | +const fetchPackageInfo = async (packageName: string): Promise<PackageInfo> => { |
| 18 | + const NO_NPM_README_PLACEHOLDER = 'ERROR: No README data found!'; |
| 19 | + |
| 20 | + const encodedName = encodeURIComponent(packageName); |
| 21 | + console.debug(`Loading NPM package info: ${packageName}`); |
| 22 | + const [packageInfo, { downloads }] = await Promise.all([ |
| 23 | + fetch(`https://registry.npmjs.org/${encodedName}`).then(response => response.json()), |
| 24 | + fetch(`https://api.npmjs.org/downloads/point/last-week/${encodedName}`).then(response => response.json()), |
| 25 | + ]); |
| 26 | + const { readme, time, description } = packageInfo; |
| 27 | + const latestVersion = packageInfo['dist-tags'].latest; |
| 28 | + |
| 29 | + return { |
| 30 | + readme: |
| 31 | + (readme !== NO_NPM_README_PLACEHOLDER && readme) || // for some reason top level "readme" can be empty string, so we get the latest version readme |
| 32 | + Object.values(packageInfo.versions as { readme?: string; version: string }[]) |
| 33 | + .reverse() |
| 34 | + .find(curr => { |
| 35 | + const isReadmeExist = curr.readme && curr.readme !== NO_NPM_README_PLACEHOLDER; |
| 36 | + if (isReadmeExist) { |
| 37 | + return semver.lte(curr.version, latestVersion); |
| 38 | + } |
| 39 | + })?.readme || |
| 40 | + '', |
| 41 | + createdAt: time.created, |
| 42 | + updatedAt: time.modified, |
| 43 | + description, |
| 44 | + weeklyNPMDownloads: downloads, |
| 45 | + }; |
| 46 | +}; |
| 47 | + |
| 48 | +const result: Record<string, PackageInfo> = {}; |
| 49 | +for (let [identifier, pkg] of Object.entries(PACKAGES)) { |
| 50 | + const packageInfo = await fetchPackageInfo(pkg.npmPackage); |
| 51 | + result[identifier] = packageInfo; |
| 52 | + |
| 53 | + await (async function randomSleep({ minMs, maxMs }: { minMs: number; maxMs: number }): Promise<void> { |
| 54 | + function sleep(ms: number): Promise<void> { |
| 55 | + return new Promise(resolve => setTimeout(resolve, ms)); |
| 56 | + } |
| 57 | + |
| 58 | + const delay = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs; |
| 59 | + await sleep(delay); |
| 60 | + })({ minMs: 100, maxMs: 5000 }); |
| 61 | +} |
| 62 | + |
| 63 | +writeFileSync( |
| 64 | + path.join(process.cwd(), 'src', 'lib', 'packages-info.generated.ts'), |
| 65 | + `export const packagesInfo= ${JSON.stringify(result)}`, |
| 66 | + { |
| 67 | + encoding: 'utf8', |
| 68 | + } |
| 69 | +); |
0 commit comments