UNPKG

dependencies-hierarchy

Version:

Creates a dependencies hierarchy for a symlinked `node_modules`

314 lines 12.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildDependenciesHierarchy = void 0; const path_1 = __importDefault(require("path")); const lockfile_file_1 = require("@pnpm/lockfile-file"); const lockfile_utils_1 = require("@pnpm/lockfile-utils"); const modules_yaml_1 = require("@pnpm/modules-yaml"); const normalize_registries_1 = require("@pnpm/normalize-registries"); const read_modules_dir_1 = require("@pnpm/read-modules-dir"); const read_package_json_1 = require("@pnpm/read-package-json"); const types_1 = require("@pnpm/types"); const dependency_path_1 = require("dependency-path"); const normalize_path_1 = __importDefault(require("normalize-path")); const realpath_missing_1 = __importDefault(require("realpath-missing")); const resolve_link_target_1 = __importDefault(require("resolve-link-target")); async function buildDependenciesHierarchy(projectPaths, maybeOpts) { if (!maybeOpts?.lockfileDir) { throw new TypeError('opts.lockfileDir is required'); } const modulesDir = await (0, realpath_missing_1.default)(path_1.default.join(maybeOpts.lockfileDir, 'node_modules')); const modules = await (0, modules_yaml_1.readModulesManifest)(modulesDir); const registries = (0, normalize_registries_1.normalizeRegistries)({ ...maybeOpts?.registries, ...modules?.registries, }); const currentLockfile = (modules?.virtualStoreDir && await (0, lockfile_file_1.readCurrentLockfile)(modules.virtualStoreDir, { ignoreIncompatible: false })) ?? null; const result = {}; if (!currentLockfile) { for (const projectPath of projectPaths) { result[projectPath] = {}; } return result; } const opts = { depth: maybeOpts.depth || 0, include: maybeOpts.include ?? { dependencies: true, devDependencies: true, optionalDependencies: true, }, lockfileDir: maybeOpts.lockfileDir, registries, search: maybeOpts.search, skipped: new Set(modules?.skipped ?? []), }; (await Promise.all(projectPaths.map(async (projectPath) => { return [ projectPath, await dependenciesHierarchyForPackage(projectPath, currentLockfile, opts), ]; }))).forEach(([projectPath, dependenciesHierarchy]) => { result[projectPath] = dependenciesHierarchy; }); return result; } exports.buildDependenciesHierarchy = buildDependenciesHierarchy; async function dependenciesHierarchyForPackage(projectPath, currentLockfile, opts) { const importerId = (0, lockfile_file_1.getLockfileImporterId)(opts.lockfileDir, projectPath); if (!currentLockfile.importers[importerId]) return {}; const modulesDir = path_1.default.join(projectPath, 'node_modules'); const savedDeps = getAllDirectDependencies(currentLockfile.importers[importerId]); const allDirectDeps = await (0, read_modules_dir_1.readModulesDir)(modulesDir) ?? []; const unsavedDeps = allDirectDeps.filter((directDep) => !savedDeps[directDep]); const wantedLockfile = await (0, lockfile_file_1.readWantedLockfile)(opts.lockfileDir, { ignoreIncompatible: false }) ?? { packages: {} }; const getChildrenTree = getTree.bind(null, { currentDepth: 1, currentPackages: currentLockfile.packages ?? {}, includeOptionalDependencies: opts.include.optionalDependencies, lockfileDir: opts.lockfileDir, maxDepth: opts.depth, modulesDir, registries: opts.registries, search: opts.search, skipped: opts.skipped, wantedPackages: wantedLockfile.packages ?? {}, }); const result = {}; for (const dependenciesField of types_1.DEPENDENCIES_FIELDS.sort().filter(dependenciedField => opts.include[dependenciedField])) { const topDeps = currentLockfile.importers[importerId][dependenciesField] ?? {}; result[dependenciesField] = []; Object.entries(topDeps).forEach(([alias, ref]) => { const { packageInfo, packageAbsolutePath } = getPkgInfo({ alias, currentPackages: currentLockfile.packages ?? {}, lockfileDir: opts.lockfileDir, modulesDir, ref, registries: opts.registries, skipped: opts.skipped, wantedPackages: wantedLockfile.packages ?? {}, }); let newEntry = null; const matchedSearched = opts.search?.(packageInfo); if (packageAbsolutePath === null) { if ((opts.search != null) && !matchedSearched) return; newEntry = packageInfo; } else { const relativeId = (0, dependency_path_1.refToRelative)(ref, alias); if (relativeId) { const dependencies = getChildrenTree([relativeId], relativeId); if (dependencies.length > 0) { newEntry = { ...packageInfo, dependencies, }; } else if ((opts.search == null) || matchedSearched) { newEntry = packageInfo; } } } if (newEntry != null) { if (matchedSearched) { newEntry.searched = true; } result[dependenciesField].push(newEntry); } }); } await Promise.all(unsavedDeps.map(async (unsavedDep) => { let pkgPath = path_1.default.join(modulesDir, unsavedDep); let version; try { pkgPath = await (0, resolve_link_target_1.default)(pkgPath); version = `link:${(0, normalize_path_1.default)(path_1.default.relative(projectPath, pkgPath))}`; } catch (err) { // eslint-disable-line // if error happened. The package is not a link const pkg = await (0, read_package_json_1.safeReadPackageJsonFromDir)(pkgPath); version = pkg?.version ?? 'undefined'; } const pkg = { alias: unsavedDep, isMissing: false, isPeer: false, isSkipped: false, name: unsavedDep, path: pkgPath, version, }; const matchedSearched = opts.search?.(pkg); if ((opts.search != null) && !matchedSearched) return; const newEntry = pkg; if (matchedSearched) { newEntry.searched = true; } result.unsavedDependencies = result.unsavedDependencies ?? []; result.unsavedDependencies.push(newEntry); })); return result; } function getAllDirectDependencies(projectSnapshot) { return { ...projectSnapshot.dependencies, ...projectSnapshot.devDependencies, ...projectSnapshot.optionalDependencies, }; } function getTree(opts, keypath, parentId) { const dependenciesCache = new Map(); return getTreeHelper(dependenciesCache, opts, keypath, parentId).dependencies; } function getTreeHelper(dependenciesCache, opts, keypath, parentId) { const result = { dependencies: [] }; if (opts.currentDepth > opts.maxDepth || !opts.currentPackages || !opts.currentPackages[parentId]) return result; const deps = !opts.includeOptionalDependencies ? opts.currentPackages[parentId].dependencies : { ...opts.currentPackages[parentId].dependencies, ...opts.currentPackages[parentId].optionalDependencies, }; if (deps == null) return result; const getChildrenTree = getTreeHelper.bind(null, dependenciesCache, { ...opts, currentDepth: opts.currentDepth + 1, }); const peers = new Set(Object.keys(opts.currentPackages[parentId].peerDependencies ?? {})); Object.entries(deps).forEach(([alias, ref]) => { const { packageInfo, packageAbsolutePath } = getPkgInfo({ alias, currentPackages: opts.currentPackages, lockfileDir: opts.lockfileDir, modulesDir: opts.modulesDir, peers, ref, registries: opts.registries, skipped: opts.skipped, wantedPackages: opts.wantedPackages, }); let circular; const matchedSearched = opts.search?.(packageInfo); let newEntry = null; if (packageAbsolutePath === null) { circular = false; if (opts.search == null || matchedSearched) { newEntry = packageInfo; } } else { let dependencies; const relativeId = (0, dependency_path_1.refToRelative)(ref, alias); // we know for sure that relative is not null if pkgPath is not null circular = keypath.includes(relativeId); if (circular) { dependencies = []; } else { dependencies = dependenciesCache.get(packageAbsolutePath); if (dependencies == null) { const children = getChildrenTree(keypath.concat([relativeId]), relativeId); dependencies = children.dependencies; if (children.circular) { result.circular = true; } else { dependenciesCache.set(packageAbsolutePath, dependencies); } } } if (dependencies.length > 0) { newEntry = { ...packageInfo, dependencies, }; } else if ((opts.search == null) || matchedSearched) { newEntry = packageInfo; } } if (newEntry != null) { if (circular) { newEntry.circular = true; result.circular = true; } if (matchedSearched) { newEntry.searched = true; } result.dependencies.push(newEntry); } }); return result; } function getPkgInfo(opts) { let name; let version; let resolved; let dev; let optional; let isSkipped = false; let isMissing = false; const depPath = (0, dependency_path_1.refToRelative)(opts.ref, opts.alias); if (depPath) { let pkgSnapshot; if (opts.currentPackages[depPath]) { pkgSnapshot = opts.currentPackages[depPath]; const parsed = (0, lockfile_utils_1.nameVerFromPkgSnapshot)(depPath, pkgSnapshot); name = parsed.name; version = parsed.version; } else { pkgSnapshot = opts.wantedPackages[depPath]; if (pkgSnapshot) { const parsed = (0, lockfile_utils_1.nameVerFromPkgSnapshot)(depPath, pkgSnapshot); name = parsed.name; version = parsed.version; } else { name = opts.alias; version = opts.ref; } isMissing = true; isSkipped = opts.skipped.has(depPath); } resolved = (0, lockfile_utils_1.pkgSnapshotToResolution)(depPath, pkgSnapshot, opts.registries)['tarball']; dev = pkgSnapshot.dev; optional = pkgSnapshot.optional; } else { name = opts.alias; version = opts.ref; } const packageAbsolutePath = (0, dependency_path_1.refToRelative)(opts.ref, opts.alias); const packageInfo = { alias: opts.alias, isMissing, isPeer: Boolean(opts.peers?.has(opts.alias)), isSkipped, name, path: depPath ? path_1.default.join(opts.modulesDir, '.pnpm', (0, dependency_path_1.depPathToFilename)(depPath)) : path_1.default.join(opts.modulesDir, '..', opts.ref.slice(5)), version, }; if (resolved) { packageInfo['resolved'] = resolved; } if (optional === true) { packageInfo['optional'] = true; } if (typeof dev === 'boolean') { packageInfo['dev'] = dev; } return { packageAbsolutePath, packageInfo, }; } //# sourceMappingURL=index.js.map