npm-install-size
Version:
Check the install size of any NPM package before installing it.
73 lines (67 loc) • 2.63 kB
JavaScript
import fetch from 'node-fetch';
import tar from 'tar';
import fs from 'fs/promises';
export async function retry(fn, retries = 3, delay = 500) {
let lastErr;
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (err) {
lastErr = err;
if (i < retries - 1) await new Promise(res => setTimeout(res, delay));
}
}
throw lastErr;
}
export function parsePkgArg(arg) {
// Handles scoped packages and version, e.g. @babel/core@7.0.0
const match = arg.match(/^(@[^/]+\/[^@]+|[^@]+)(?:@(.*))?$/);
if (!match) return { name: arg, version: undefined };
return { name: match[1], version: match[2] };
}
export async function getLatestTarballUrl(pkgArg) {
const { name, version } = parsePkgArg(pkgArg);
const encoded = encodeURIComponent(name);
const url = `https://registry.npmjs.org/${encoded}`;
const res = await retry(() => fetch(url));
if (!res.ok) throw new Error(`Package not found`);
const data = await res.json();
let ver = version || data['dist-tags']?.latest;
if (!ver || !data.versions[ver]) throw new Error(`Version not found`);
return { tarball: data.versions[ver].dist.tarball, version: ver };
}
export async function downloadAndExtractTarball(tarballUrl, destDir) {
const res = await retry(() => fetch(tarballUrl));
if (!res.ok) throw new Error(`Failed to download tarball`);
await fs.mkdir(destDir, { recursive: true });
return new Promise((resolve, reject) => {
const extractStream = tar.x({ cwd: destDir, strip: 1 });
res.body.pipe(extractStream);
res.body.on('error', reject);
extractStream.on('finish', resolve);
extractStream.on('error', reject);
});
}
export async function fetchPackageVersions(pkgName) {
const encoded = encodeURIComponent(pkgName);
const url = `https://registry.npmjs.org/${encoded}`;
const res = await retry(() => fetch(url));
if (!res.ok) throw new Error(`Package not found`);
const data = await res.json();
return Object.keys(data.versions).sort((a, b) => {
// Sort semver descending
return b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' });
});
}
export async function fetchPackageMeta(pkgArg) {
const { name, version } = parsePkgArg(pkgArg);
const encoded = encodeURIComponent(name);
const url = `https://registry.npmjs.org/${encoded}`;
const res = await retry(() => fetch(url));
if (!res.ok) throw new Error(`Package not found`);
const data = await res.json();
if (version && data.versions[version]) {
return data.versions[version];
}
return data;
}