UNPKG

5.25 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const constants_1 = require("@pnpm/constants");
4const link_bins_1 = require("@pnpm/link-bins");
5const lockfile_utils_1 = require("@pnpm/lockfile-utils");
6const lockfile_walker_1 = require("@pnpm/lockfile-walker");
7const logger_1 = require("@pnpm/logger");
8const pkgid_to_filename_1 = require("@pnpm/pkgid-to-filename");
9const symlink_dependency_1 = require("@pnpm/symlink-dependency");
10const dp = require("dependency-path");
11const path = require("path");
12const R = require("ramda");
13async function hoistByLockfile(match, opts) {
14 if (!opts.lockfile.packages)
15 return {};
16 const deps = await getDependencies(lockfile_walker_1.default(opts.lockfile, Object.keys(opts.lockfile.importers)), 0, {
17 getIndependentPackageLocation: opts.getIndependentPackageLocation,
18 lockfileDir: opts.lockfileDir,
19 registries: opts.registries,
20 virtualStoreDir: opts.virtualStoreDir,
21 });
22 const aliasesByDependencyPath = await hoistGraph(deps, opts.lockfile.importers['.'].specifiers, {
23 dryRun: false,
24 match,
25 modulesDir: opts.modulesDir,
26 });
27 const bin = path.join(opts.modulesDir, '.bin');
28 const warn = (message) => logger_1.default.warn({ message, prefix: path.join(opts.modulesDir, '../..') });
29 try {
30 await link_bins_1.default(opts.modulesDir, bin, { allowExoticManifests: true, warn });
31 }
32 catch (err) {
33 // Some packages generate their commands with lifecycle hooks.
34 // At this stage, such commands are not generated yet.
35 // For now, we don't hoist such generated commands.
36 // Related issue: https://github.com/pnpm/pnpm/issues/2071
37 }
38 return aliasesByDependencyPath;
39}
40exports.default = hoistByLockfile;
41async function getDependencies(step, depth, opts) {
42 const deps = [];
43 const nextSteps = [];
44 for (const { pkgSnapshot, relDepPath, next } of step.dependencies) {
45 const absolutePath = dp.resolve(opts.registries, relDepPath);
46 const pkgName = lockfile_utils_1.nameVerFromPkgSnapshot(relDepPath, pkgSnapshot).name;
47 const modules = path.join(opts.virtualStoreDir, pkgid_to_filename_1.default(absolutePath, opts.lockfileDir), 'node_modules');
48 const independent = opts.getIndependentPackageLocation && lockfile_utils_1.packageIsIndependent(pkgSnapshot);
49 const allDeps = {
50 ...pkgSnapshot.dependencies,
51 ...pkgSnapshot.optionalDependencies,
52 };
53 deps.push({
54 absolutePath,
55 children: Object.keys(allDeps).reduce((children, alias) => {
56 children[alias] = dp.refToAbsolute(allDeps[alias], alias, opts.registries);
57 return children;
58 }, {}),
59 depth,
60 location: !independent
61 ? path.join(modules, pkgName)
62 : await opts.getIndependentPackageLocation(pkgSnapshot.id || absolutePath, pkgName),
63 name: pkgName,
64 });
65 nextSteps.push(next());
66 }
67 for (const relDepPath of step.missing) {
68 // It might make sense to fail if the depPath is not in the skipped list from .modules.yaml
69 // However, the skipped list currently contains package IDs, not dep paths.
70 logger_1.default.debug({ message: `No entry for "${relDepPath}" in ${constants_1.WANTED_LOCKFILE}` });
71 }
72 return (await Promise.all(nextSteps.map((nextStep) => getDependencies(nextStep, depth + 1, opts)))).reduce((acc, deps) => [...acc, ...deps], deps);
73}
74async function hoistGraph(depNodes, currentSpecifiers, opts) {
75 const hoistedAliases = new Set(R.keys(currentSpecifiers));
76 const aliasesByDependencyPath = {};
77 await Promise.all(depNodes
78 // sort by depth and then alphabetically
79 .sort((a, b) => {
80 const depthDiff = a.depth - b.depth;
81 return depthDiff === 0 ? a.name.localeCompare(b.name) : depthDiff;
82 })
83 // build the alias map and the id map
84 .map((depNode) => {
85 for (const childAlias of Object.keys(depNode.children)) {
86 if (!opts.match(childAlias))
87 continue;
88 // if this alias has already been taken, skip it
89 if (hoistedAliases.has(childAlias)) {
90 continue;
91 }
92 hoistedAliases.add(childAlias);
93 const childPath = depNode.children[childAlias];
94 if (!aliasesByDependencyPath[childPath]) {
95 aliasesByDependencyPath[childPath] = [];
96 }
97 aliasesByDependencyPath[childPath].push(childAlias);
98 }
99 return depNode;
100 })
101 .map(async (depNode) => {
102 const pkgAliases = aliasesByDependencyPath[depNode.absolutePath];
103 if (!pkgAliases) {
104 return;
105 }
106 // TODO when putting logs back in for hoisted packages, you've to put back the condition inside the map,
107 // TODO look how it is done in linkPackages
108 if (!opts.dryRun) {
109 await Promise.all(pkgAliases.map(async (pkgAlias) => {
110 await symlink_dependency_1.default(depNode.location, opts.modulesDir, pkgAlias);
111 }));
112 }
113 }));
114 return aliasesByDependencyPath;
115}