UNPKG

10.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const lockfile_file_1 = require("@pnpm/lockfile-file");
4const lockfile_utils_1 = require("@pnpm/lockfile-utils");
5const modules_yaml_1 = require("@pnpm/modules-yaml");
6const read_modules_dir_1 = require("@pnpm/read-modules-dir");
7const types_1 = require("@pnpm/types");
8const utils_1 = require("@pnpm/utils");
9const dependency_path_1 = require("dependency-path");
10const normalizePath = require("normalize-path");
11const path = require("path");
12const resolveLinkTarget = require("resolve-link-target");
13async function dependenciesHierarchy(projectPaths, maybeOpts) {
14 var _a;
15 if (!maybeOpts || !maybeOpts.lockfileDir) {
16 throw new TypeError('opts.lockfileDir is required');
17 }
18 const modulesDir = await utils_1.realNodeModulesDir(maybeOpts.lockfileDir);
19 const modules = await modules_yaml_1.read(modulesDir);
20 const registries = utils_1.normalizeRegistries({
21 ...maybeOpts && maybeOpts.registries,
22 ...modules && modules.registries,
23 });
24 const currentLockfile = ((_a = modules) === null || _a === void 0 ? void 0 : _a.virtualStoreDir) && await lockfile_file_1.readCurrentLockfile(modules.virtualStoreDir, { ignoreIncompatible: false }) || null;
25 const result = {};
26 if (!currentLockfile) {
27 for (let projectPath of projectPaths) {
28 result[projectPath] = {};
29 }
30 return result;
31 }
32 const opts = {
33 depth: maybeOpts.depth || 0,
34 include: maybeOpts.include || {
35 dependencies: true,
36 devDependencies: true,
37 optionalDependencies: true,
38 },
39 lockfileDir: maybeOpts.lockfileDir,
40 registries,
41 search: maybeOpts.search,
42 skipped: new Set(modules && modules.skipped || []),
43 };
44 (await Promise.all(projectPaths.map(async (projectPath) => {
45 return [
46 projectPath,
47 await dependenciesHierarchyForPackage(projectPath, currentLockfile, opts),
48 ];
49 }))).forEach(([projectPath, dependenciesHierarchy]) => {
50 result[projectPath] = dependenciesHierarchy;
51 });
52 return result;
53}
54exports.default = dependenciesHierarchy;
55async function dependenciesHierarchyForPackage(projectPath, currentLockfile, opts) {
56 const importerId = lockfile_file_1.getLockfileImporterId(opts.lockfileDir, projectPath);
57 if (!currentLockfile.importers[importerId])
58 return {};
59 const modulesDir = path.join(projectPath, 'node_modules');
60 const savedDeps = getAllDirectDependencies(currentLockfile.importers[importerId]);
61 const allDirectDeps = await read_modules_dir_1.default(modulesDir) || [];
62 const unsavedDeps = allDirectDeps.filter((directDep) => !savedDeps[directDep]);
63 const wantedLockfile = await lockfile_file_1.readWantedLockfile(opts.lockfileDir, { ignoreIncompatible: false }) || { packages: {} };
64 const getChildrenTree = getTree.bind(null, {
65 currentDepth: 1,
66 currentPackages: currentLockfile.packages || {},
67 includeOptionalDependencies: opts.include.optionalDependencies === true,
68 maxDepth: opts.depth,
69 modulesDir,
70 registries: opts.registries,
71 search: opts.search,
72 skipped: opts.skipped,
73 wantedPackages: wantedLockfile.packages || {},
74 });
75 const result = {};
76 for (const dependenciesField of types_1.DEPENDENCIES_FIELDS.sort().filter(dependenciedField => opts.include[dependenciedField])) {
77 const topDeps = currentLockfile.importers[importerId][dependenciesField] || {};
78 result[dependenciesField] = [];
79 Object.keys(topDeps).forEach((alias) => {
80 const { packageInfo, packageAbsolutePath } = getPkgInfo({
81 alias,
82 currentPackages: currentLockfile.packages || {},
83 modulesDir,
84 ref: topDeps[alias],
85 registries: opts.registries,
86 skipped: opts.skipped,
87 wantedPackages: wantedLockfile.packages || {},
88 });
89 let newEntry = null;
90 const matchedSearched = opts.search && opts.search(packageInfo);
91 if (packageAbsolutePath === null) {
92 if (opts.search && !matchedSearched)
93 return;
94 newEntry = packageInfo;
95 }
96 else {
97 const relativeId = dependency_path_1.refToRelative(topDeps[alias], alias);
98 if (relativeId) {
99 const dependencies = getChildrenTree([relativeId], relativeId);
100 if (dependencies.length) {
101 newEntry = {
102 ...packageInfo,
103 dependencies,
104 };
105 }
106 else if (!opts.search || matchedSearched) {
107 newEntry = packageInfo;
108 }
109 }
110 }
111 if (newEntry) {
112 if (matchedSearched) {
113 newEntry.searched = true;
114 }
115 result[dependenciesField].push(newEntry);
116 }
117 });
118 }
119 await Promise.all(unsavedDeps.map(async (unsavedDep) => {
120 let pkgPath = path.join(modulesDir, unsavedDep);
121 let version;
122 try {
123 pkgPath = await resolveLinkTarget(pkgPath);
124 version = `link:${normalizePath(path.relative(projectPath, pkgPath))}`;
125 }
126 catch (err) {
127 // if error happened. The package is not a link
128 const pkg = await utils_1.safeReadPackageFromDir(pkgPath);
129 version = pkg && pkg.version || 'undefined';
130 }
131 const pkg = {
132 alias: unsavedDep,
133 isMissing: false,
134 isPeer: false,
135 isSkipped: false,
136 name: unsavedDep,
137 path: pkgPath,
138 version,
139 };
140 const matchedSearched = opts.search && opts.search(pkg);
141 if (opts.search && !matchedSearched)
142 return;
143 const newEntry = pkg;
144 if (matchedSearched) {
145 newEntry.searched = true;
146 }
147 result.unsavedDependencies = result.unsavedDependencies || [];
148 result.unsavedDependencies.push(newEntry);
149 }));
150 return result;
151}
152function getAllDirectDependencies(lockfileImporter) {
153 return {
154 ...lockfileImporter.dependencies,
155 ...lockfileImporter.devDependencies,
156 ...lockfileImporter.optionalDependencies,
157 };
158}
159function getTree(opts, keypath, parentId) {
160 if (opts.currentDepth > opts.maxDepth || !opts.currentPackages || !opts.currentPackages[parentId])
161 return [];
162 const deps = opts.includeOptionalDependencies === false
163 ? opts.currentPackages[parentId].dependencies
164 : {
165 ...opts.currentPackages[parentId].dependencies,
166 ...opts.currentPackages[parentId].optionalDependencies,
167 };
168 if (!deps)
169 return [];
170 const getChildrenTree = getTree.bind(null, {
171 ...opts,
172 currentDepth: opts.currentDepth + 1,
173 });
174 const peers = new Set(Object.keys(opts.currentPackages[parentId].peerDependencies || {}));
175 const result = [];
176 Object.keys(deps).forEach((alias) => {
177 const { packageInfo, packageAbsolutePath } = getPkgInfo({
178 alias,
179 currentPackages: opts.currentPackages,
180 modulesDir: opts.modulesDir,
181 peers,
182 ref: deps[alias],
183 registries: opts.registries,
184 skipped: opts.skipped,
185 wantedPackages: opts.wantedPackages,
186 });
187 let circular;
188 const matchedSearched = opts.search && opts.search(packageInfo);
189 let newEntry = null;
190 if (packageAbsolutePath === null) {
191 circular = false;
192 newEntry = packageInfo;
193 }
194 else {
195 const relativeId = dependency_path_1.refToRelative(deps[alias], alias); // we know for sure that relative is not null if pkgPath is not null
196 circular = keypath.includes(relativeId);
197 const dependencies = circular ? [] : getChildrenTree(keypath.concat([relativeId]), relativeId);
198 if (dependencies.length) {
199 newEntry = {
200 ...packageInfo,
201 dependencies,
202 };
203 }
204 else if (!opts.search || matchedSearched) {
205 newEntry = packageInfo;
206 }
207 }
208 if (newEntry) {
209 if (circular) {
210 newEntry.circular = true;
211 }
212 if (matchedSearched) {
213 newEntry.searched = true;
214 }
215 result.push(newEntry);
216 }
217 });
218 return result;
219}
220function getPkgInfo(opts) {
221 let name;
222 let version;
223 let resolved = undefined;
224 let dev = undefined;
225 let optional = undefined;
226 let isSkipped = false;
227 let isMissing = false;
228 const relDepPath = dependency_path_1.refToRelative(opts.ref, opts.alias);
229 if (relDepPath) {
230 let pkgSnapshot;
231 if (opts.currentPackages[relDepPath]) {
232 pkgSnapshot = opts.currentPackages[relDepPath];
233 const parsed = lockfile_utils_1.nameVerFromPkgSnapshot(relDepPath, pkgSnapshot);
234 name = parsed.name;
235 version = parsed.version;
236 }
237 else {
238 pkgSnapshot = opts.wantedPackages[relDepPath];
239 const parsed = lockfile_utils_1.nameVerFromPkgSnapshot(relDepPath, pkgSnapshot);
240 name = parsed.name;
241 version = parsed.version;
242 isMissing = true;
243 isSkipped = opts.skipped.has(relDepPath);
244 }
245 resolved = lockfile_utils_1.pkgSnapshotToResolution(relDepPath, pkgSnapshot, opts.registries)['tarball'];
246 dev = pkgSnapshot.dev;
247 optional = pkgSnapshot.optional;
248 }
249 else {
250 name = opts.alias;
251 version = opts.ref;
252 }
253 const packageAbsolutePath = dependency_path_1.refToAbsolute(opts.ref, opts.alias, opts.registries);
254 const packageInfo = {
255 alias: opts.alias,
256 isMissing,
257 isPeer: Boolean(opts.peers && opts.peers.has(opts.alias)),
258 isSkipped,
259 name,
260 path: packageAbsolutePath && path.join(opts.modulesDir, '.pnpm', packageAbsolutePath) || path.join(opts.modulesDir, '..', opts.ref.substr(5)),
261 version,
262 };
263 if (resolved) {
264 packageInfo['resolved'] = resolved;
265 }
266 if (optional === true) {
267 packageInfo['optional'] = true;
268 }
269 if (typeof dev === 'boolean') {
270 packageInfo['dev'] = dev;
271 }
272 return {
273 packageAbsolutePath,
274 packageInfo,
275 };
276}