@sizium/core
Version:
Get the actual size of any local or remote package
181 lines (172 loc) • 6.12 kB
JavaScript
;
const semver = require('semver');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const semver__default = /*#__PURE__*/_interopDefaultCompat(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__default.valid(version)) selectedVersion = version;
else {
selectedVersion = semver__default.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;
}
}
exports.PackageSuper = PackageSuper;