UNPKG

@sizium/core

Version:

Get the actual size of any local or remote package

175 lines (169 loc) 5.89 kB
import semver from 'semver'; const ERROR_ID = { INVALID_PKG_NAME: "INVALID_PKG_NAME", GETTING_PKG_NAME: "GETTING_PKG_NAME", GETTING_REGISTRY_DATA: "GETTING_REGISTRY_DATA", GETTING_LOCAL_DATA: "GETTING_LOCAL_DATA" }; const LIFE_CYCLE_SCRIPTS = [ "preinstall", "install", "postinstall", "prepublish", "preprepare", "prepare", "postprepare" ]; class TypedError extends Error { data; constructor(message, data) { super(message); this.data = data; Error.captureStackTrace(this, this.constructor); } } const normalizeRepositoryUrl = (url) => { if (!url) return void 0; try { const parsedUrl = new URL(url); parsedUrl.protocol = "https:"; if (parsedUrl.pathname.endsWith(".git")) parsedUrl.pathname = parsedUrl.pathname.slice(0, -4); return parsedUrl.toString(); } catch (_error) { return void 0; } }; class SiziumError extends TypedError { } class PackageSuper { constructor(input, opts) { this.input = input; this.opts = opts; } ERROR_ID = ERROR_ID; Error = SiziumError; processedPackages = /* @__PURE__ */ new Set(); LIFE_CYCLE_SCRIPTS = LIFE_CYCLE_SCRIPTS; getMainPkgData(allPackages) { const totalSize = allPackages.reduce((sum, pkg) => { const size = pkg.unpackedSize ?? 0; return sum + size; }, 0); return { id: allPackages[0].name, packageNum: allPackages.length, size: totalSize, sizeKB: totalSize / 1e3, sizeMB: totalSize / 1e6, packages: allPackages }; } getPkgData(data, level = 0, unpackedSize, installedBy) { const lcScripts = {}; if (data.scripts) { const scripts = Object.keys(data.scripts); scripts.forEach((script) => { if (this.LIFE_CYCLE_SCRIPTS.includes(script)) lcScripts[script] = data.scripts[script]; }); } const size = unpackedSize ?? 0; return { name: data.name, version: data.version, description: data.description, license: data.license, isESM: data.type === "module", isCommonJS: data.type !== "module", types: !!data.types, author: data.author && data.author.url ? { name: data.author.name, url: data.author.url } : void 0, url: { homepage: normalizeRepositoryUrl(data.homepage), repository: normalizeRepositoryUrl( typeof data.repository === "string" ? data.repository : data.repository?.url ), funding: Array.isArray(data.funding) ? typeof data.funding[0] === "string" ? data.funding[0] : data.funding[0]?.url : typeof data.funding === "string" ? data.funding : data.funding?.url, npm: `https://www.npmjs.com/package/${data.name}/v/${data.version}`, unpkg: `https://unpkg.com/${data.name}@${data.version}/` }, unpackedSize: size, unpackedSizeKB: size / 1e3, unpackedSizeMB: size / 1e6, dependencies: data.dependencies, devDependencies: data.devDependencies, lifeCycleScripts: Object.keys(lcScripts).length ? lcScripts : void 0, installedBy: installedBy === void 0 ? void 0 : Array.isArray(installedBy) ? installedBy : [installedBy], level }; } #getVersion(version, availableVersions) { let selectedVersion; if (semver.valid(version)) selectedVersion = version; else { selectedVersion = semver.maxSatisfying(availableVersions, version) || void 0; } return selectedVersion?.replace(/^v(?=\d)/, ""); } async getRegistryData(packageName, version, level = 0, installedBy) { try { const response = await fetch(`https://registry.npmjs.org/${packageName}`); if (!response.ok) { if (response.status === 404) throw new Error(`Package "${packageName}" not found (404 Not Found).`); else throw new Error(`Failed to fetch data. HTTP Status: ${response.status}`); } const res = await response.json(); const selectedVersion = this.#getVersion(version, Object.keys(res.versions)) || res["dist-tags"].latest || "latest"; const data = res.versions[selectedVersion]; if (!data) throw new Error(`Version ${version} (${selectedVersion}) not found`); const size = data.dist?.unpackedSize; const secureSize = typeof size === "string" ? Number(size) : typeof size === "number" ? size : void 0; return this.getPkgData(data, level, secureSize, installedBy); } catch (e) { throw new this.Error( this.ERROR_ID.GETTING_REGISTRY_DATA, { msg: `Error getting "${packageName}" dependence data. ${e instanceof Error ? e.message : ""}`, e } ); } } async #processDependencies(dependencies, level, parentName) { const packages = []; const promises = Object.entries(dependencies).map(async ([depName, depVersion]) => { try { const packageKey = `${depName}@${depVersion}`; if (!depVersion) return; if (this.processedPackages.has(packageKey)) return; this.processedPackages.add(packageKey); const depData = await this.getRegistryData(depName, depVersion, level, parentName); packages.push(depData); if (depData.dependencies) { const subDeps = await this.#processDependencies(depData.dependencies, level + 1, depName); packages.push(...subDeps); } } catch (e) { if (this.opts?.skipError) return; throw e; } }); await Promise.all(promises); return packages; } async getPackagesData(mainPackage) { const allPackages = [mainPackage]; this.processedPackages.add(`${mainPackage.name}@${mainPackage.version}`); if (mainPackage.dependencies) { const deps = await this.#processDependencies(mainPackage.dependencies, 1, mainPackage.name); allPackages.push(...deps); } return allPackages; } } export { PackageSuper as P };