UNPKG

6.64 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 matcher_1 = require("@pnpm/matcher");
9const pkgid_to_filename_1 = require("@pnpm/pkgid-to-filename");
10const symlink_dependency_1 = require("@pnpm/symlink-dependency");
11const dp = require("dependency-path");
12const path = require("path");
13const R = require("ramda");
14async function hoistByLockfile(opts) {
15 var _a, _b;
16 if (!opts.lockfile.packages)
17 return {};
18 const { directDeps, step } = lockfile_walker_1.default(opts.lockfile, Object.keys(opts.lockfile.importers));
19 const deps = [
20 {
21 children: directDeps
22 .reduce((acc, { alias, depPath }) => {
23 if (!acc[alias]) {
24 acc[alias] = depPath;
25 }
26 return acc;
27 }, {}),
28 depPath: '',
29 depth: -1,
30 },
31 ...await getDependencies(step, 0),
32 ];
33 const getAliasHoistType = createGetAliasHoistType(opts.publicHoistPattern, opts.privateHoistPattern);
34 const hoistedDependencies = await hoistGraph(deps, (_b = (_a = opts.lockfile.importers['.']) === null || _a === void 0 ? void 0 : _a.specifiers) !== null && _b !== void 0 ? _b : {}, {
35 getAliasHoistType,
36 });
37 await symlinkHoistedDependencies(hoistedDependencies, {
38 lockfile: opts.lockfile,
39 lockfileDir: opts.lockfileDir,
40 privateHoistedModulesDir: opts.privateHoistedModulesDir,
41 publicHoistedModulesDir: opts.publicHoistedModulesDir,
42 virtualStoreDir: opts.virtualStoreDir,
43 });
44 // Here we only link the bins of the privately hoisted modules.
45 // The bins of the publicly hoisted modules will be linked together with
46 // the bins of the project's direct dependencies.
47 // This is possible because the publicly hoisted modules
48 // are in the same directory as the regular dependencies.
49 await linkAllBins(opts.privateHoistedModulesDir);
50 return hoistedDependencies;
51}
52exports.default = hoistByLockfile;
53function createGetAliasHoistType(publicHoistPattern, privateHoistPattern) {
54 const publicMatcher = matcher_1.default(publicHoistPattern);
55 const privateMatcher = matcher_1.default(privateHoistPattern);
56 return (alias) => {
57 if (publicMatcher(alias))
58 return 'public';
59 if (privateMatcher(alias))
60 return 'private';
61 return false;
62 };
63}
64async function linkAllBins(modulesDir) {
65 const bin = path.join(modulesDir, '.bin');
66 const warn = (message, code) => {
67 if (code === 'BINARIES_CONFLICT')
68 return;
69 logger_1.default.warn({ message, prefix: path.join(modulesDir, '../..') });
70 };
71 try {
72 await link_bins_1.default(modulesDir, bin, { allowExoticManifests: true, warn });
73 }
74 catch (err) {
75 // Some packages generate their commands with lifecycle hooks.
76 // At this stage, such commands are not generated yet.
77 // For now, we don't hoist such generated commands.
78 // Related issue: https://github.com/pnpm/pnpm/issues/2071
79 }
80}
81async function getDependencies(step, depth) {
82 const deps = [];
83 const nextSteps = [];
84 for (const { pkgSnapshot, depPath, next } of step.dependencies) {
85 const allDeps = {
86 ...pkgSnapshot.dependencies,
87 ...pkgSnapshot.optionalDependencies,
88 };
89 deps.push({
90 children: Object.entries(allDeps).reduce((children, [alias, ref]) => {
91 children[alias] = dp.refToRelative(ref, alias);
92 return children;
93 }, {}),
94 depPath,
95 depth,
96 });
97 nextSteps.push(next());
98 }
99 for (const depPath of step.missing) {
100 // It might make sense to fail if the depPath is not in the skipped list from .modules.yaml
101 // However, the skipped list currently contains package IDs, not dep paths.
102 logger_1.default.debug({ message: `No entry for "${depPath}" in ${constants_1.WANTED_LOCKFILE}` });
103 }
104 return (await Promise.all(nextSteps.map((nextStep) => getDependencies(nextStep, depth + 1)))).reduce((acc, deps) => [...acc, ...deps], deps);
105}
106async function hoistGraph(depNodes, currentSpecifiers, opts) {
107 const hoistedAliases = new Set(R.keys(currentSpecifiers));
108 const hoistedDependencies = {};
109 depNodes
110 // sort by depth and then alphabetically
111 .sort((a, b) => {
112 const depthDiff = a.depth - b.depth;
113 return depthDiff === 0 ? a.depPath.localeCompare(b.depPath) : depthDiff;
114 })
115 // build the alias map and the id map
116 .forEach((depNode) => {
117 for (const [childAlias, childPath] of Object.entries(depNode.children)) {
118 const hoist = opts.getAliasHoistType(childAlias);
119 if (!hoist)
120 continue;
121 // if this alias has already been taken, skip it
122 if (hoistedAliases.has(childAlias)) {
123 continue;
124 }
125 hoistedAliases.add(childAlias);
126 if (!hoistedDependencies[childPath]) {
127 hoistedDependencies[childPath] = {};
128 }
129 hoistedDependencies[childPath][childAlias] = hoist;
130 }
131 });
132 return hoistedDependencies;
133}
134async function symlinkHoistedDependencies(hoistedDependencies, opts) {
135 await Promise.all(Object.entries(hoistedDependencies)
136 .map(async ([depPath, pkgAliases]) => {
137 const pkgSnapshot = opts.lockfile.packages[depPath];
138 if (!pkgSnapshot) {
139 logger_1.globalWarn(`Failed to find "${depPath}" in lockfile during hoisting. `
140 + `Next aliases will not be hoisted: ${Object.keys(pkgAliases).join(', ')}`);
141 return;
142 }
143 const pkgName = lockfile_utils_1.nameVerFromPkgSnapshot(depPath, pkgSnapshot).name;
144 const modules = path.join(opts.virtualStoreDir, pkgid_to_filename_1.default(depPath, opts.lockfileDir), 'node_modules');
145 const depLocation = path.join(modules, pkgName);
146 await Promise.all(Object.entries(pkgAliases).map(async ([pkgAlias, hoistType]) => {
147 const targetDir = hoistType === 'public'
148 ? opts.publicHoistedModulesDir : opts.privateHoistedModulesDir;
149 await symlink_dependency_1.default(depLocation, targetDir, pkgAlias);
150 }));
151 }));
152}
153//# sourceMappingURL=index.js.map
\No newline at end of file