1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const lockfile_file_1 = require("@pnpm/lockfile-file");
|
4 | const lockfile_utils_1 = require("@pnpm/lockfile-utils");
|
5 | const modules_yaml_1 = require("@pnpm/modules-yaml");
|
6 | const read_modules_dir_1 = require("@pnpm/read-modules-dir");
|
7 | const types_1 = require("@pnpm/types");
|
8 | const utils_1 = require("@pnpm/utils");
|
9 | const dependency_path_1 = require("dependency-path");
|
10 | const normalizePath = require("normalize-path");
|
11 | const path = require("path");
|
12 | const resolveLinkTarget = require("resolve-link-target");
|
13 | async 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 | }
|
54 | exports.default = dependenciesHierarchy;
|
55 | async 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 |
|
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 | }
|
152 | function getAllDirectDependencies(lockfileImporter) {
|
153 | return {
|
154 | ...lockfileImporter.dependencies,
|
155 | ...lockfileImporter.devDependencies,
|
156 | ...lockfileImporter.optionalDependencies,
|
157 | };
|
158 | }
|
159 | function 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);
|
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 | }
|
220 | function 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 | }
|