@yarnpkg/plugin-pnpm 1.0.1 → 1.1.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,19 @@
1
- import { Descriptor, FetchResult, Installer, InstallPackageExtraApi, Linker, LinkOptions, Locator, MinimalLinkOptions, Package } from '@yarnpkg/core';
1
+ import { Descriptor, FetchResult, Installer, InstallPackageExtraApi, Linker, LinkOptions, Locator, LocatorHash, MinimalLinkOptions, Package } from '@yarnpkg/core';
2
2
  import { PortablePath } from '@yarnpkg/fslib';
3
3
  export declare type PnpmCustomData = {
4
- locatorByPath: Map<string, string>;
4
+ pathByLocator: Map<LocatorHash, PortablePath>;
5
+ locatorByPath: Map<PortablePath, string>;
5
6
  };
6
7
  export declare class PnpmLinker implements Linker {
7
8
  supportsPackage(pkg: Package, opts: MinimalLinkOptions): boolean;
8
9
  findPackageLocation(locator: Locator, opts: LinkOptions): Promise<PortablePath>;
9
10
  findPackageLocator(location: PortablePath, opts: LinkOptions): Promise<Locator | null>;
10
11
  makeInstaller(opts: LinkOptions): PnpmInstaller;
12
+ private isEnabled;
11
13
  }
12
14
  declare class PnpmInstaller implements Installer {
13
15
  private opts;
14
- private asyncActions;
15
- private packageLocations;
16
+ private readonly asyncActions;
16
17
  constructor(opts: LinkOptions);
17
18
  getCustomDataKey(): string;
18
19
  private customData;
@@ -34,6 +35,8 @@ declare class PnpmInstaller implements Installer {
34
35
  }>;
35
36
  attachInternalDependencies(locator: Locator, dependencies: Array<[Descriptor, Locator]>): Promise<void>;
36
37
  attachExternalDependents(locator: Locator, dependentPaths: Array<PortablePath>): Promise<void>;
37
- finalizeInstall(): Promise<void>;
38
+ finalizeInstall(): Promise<{
39
+ customData: PnpmCustomData;
40
+ }>;
38
41
  }
39
42
  export {};
package/lib/PnpmLinker.js CHANGED
@@ -1,21 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PnpmLinker = void 0;
4
- const tslib_1 = require("tslib");
5
4
  const core_1 = require("@yarnpkg/core");
6
5
  const fslib_1 = require("@yarnpkg/fslib");
7
6
  const plugin_pnp_1 = require("@yarnpkg/plugin-pnp");
8
7
  const clipanion_1 = require("clipanion");
9
- const p_limit_1 = (0, tslib_1.__importDefault)(require("p-limit"));
10
8
  class PnpmLinker {
11
9
  supportsPackage(pkg, opts) {
12
- return opts.project.configuration.get(`nodeLinker`) === `pnpm`;
10
+ return this.isEnabled(opts);
13
11
  }
14
12
  async findPackageLocation(locator, opts) {
15
- // TODO: Support soft linked packages
16
- return getPackageLocation(locator, { project: opts.project });
13
+ if (!this.isEnabled(opts))
14
+ throw new Error(`Assertion failed: Expected the pnpm linker to be enabled`);
15
+ const customDataKey = getCustomDataKey();
16
+ const customData = opts.project.installersCustomData.get(customDataKey);
17
+ if (!customData)
18
+ throw new clipanion_1.UsageError(`The project in ${core_1.formatUtils.pretty(opts.project.configuration, `${opts.project.cwd}/package.json`, core_1.formatUtils.Type.PATH)} doesn't seem to have been installed - running an install there might help`);
19
+ const packageLocation = customData.pathByLocator.get(locator.locatorHash);
20
+ if (typeof packageLocation === `undefined`)
21
+ throw new clipanion_1.UsageError(`Couldn't find ${core_1.structUtils.prettyLocator(opts.project.configuration, locator)} in the currently installed pnpm map - running an install might help`);
22
+ return packageLocation;
17
23
  }
18
24
  async findPackageLocator(location, opts) {
25
+ if (!this.isEnabled(opts))
26
+ return null;
19
27
  const customDataKey = getCustomDataKey();
20
28
  const customData = opts.project.installersCustomData.get(customDataKey);
21
29
  if (!customData)
@@ -42,14 +50,17 @@ class PnpmLinker {
42
50
  makeInstaller(opts) {
43
51
  return new PnpmInstaller(opts);
44
52
  }
53
+ isEnabled(opts) {
54
+ return opts.project.configuration.get(`nodeLinker`) === `pnpm`;
55
+ }
45
56
  }
46
57
  exports.PnpmLinker = PnpmLinker;
47
58
  class PnpmInstaller {
48
59
  constructor(opts) {
49
60
  this.opts = opts;
50
- this.asyncActions = new AsyncActions();
51
- this.packageLocations = new Map();
61
+ this.asyncActions = new core_1.miscUtils.AsyncActions(10);
52
62
  this.customData = {
63
+ pathByLocator: new Map(),
53
64
  locatorByPath: new Map(),
54
65
  };
55
66
  // Nothing to do
@@ -58,7 +69,8 @@ class PnpmInstaller {
58
69
  return getCustomDataKey();
59
70
  }
60
71
  attachCustomData(customData) {
61
- this.customData = customData;
72
+ // We don't want to attach the data because it's only used in the Linker and we'll recompute it anyways in the Installer,
73
+ // it needs to be invalidated because otherwise we'll never prune the store or we might run into various issues.
62
74
  }
63
75
  async installPackage(pkg, fetchResult, api) {
64
76
  switch (pkg.linkType) {
@@ -69,7 +81,7 @@ class PnpmInstaller {
69
81
  }
70
82
  async installPackageSoft(pkg, fetchResult, api) {
71
83
  const pkgPath = fslib_1.ppath.resolve(fetchResult.packageFs.getRealPath(), fetchResult.prefixPath);
72
- this.packageLocations.set(pkg.locatorHash, pkgPath);
84
+ this.customData.pathByLocator.set(pkg.locatorHash, pkgPath);
73
85
  return {
74
86
  packageLocation: pkgPath,
75
87
  buildDirective: null,
@@ -79,7 +91,7 @@ class PnpmInstaller {
79
91
  var _a;
80
92
  const pkgPath = getPackageLocation(pkg, { project: this.opts.project });
81
93
  this.customData.locatorByPath.set(pkgPath, core_1.structUtils.stringifyLocator(pkg));
82
- this.packageLocations.set(pkg.locatorHash, pkgPath);
94
+ this.customData.pathByLocator.set(pkg.locatorHash, pkgPath);
83
95
  api.holdFetchResult(this.asyncActions.set(pkg.locatorHash, async () => {
84
96
  await fslib_1.xfs.mkdirPromise(pkgPath, { recursive: true });
85
97
  // Copy the package source into the <root>/n_m/.store/<hash> directory, so
@@ -113,17 +125,15 @@ class PnpmInstaller {
113
125
  this.asyncActions.reduce(locator.locatorHash, async (action) => {
114
126
  // Wait that the package is properly installed before starting to copy things into it
115
127
  await action;
116
- const pkgPath = this.packageLocations.get(locator.locatorHash);
128
+ const pkgPath = this.customData.pathByLocator.get(locator.locatorHash);
117
129
  if (typeof pkgPath === `undefined`)
118
130
  throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(locator)})`);
119
131
  const nmPath = fslib_1.ppath.join(pkgPath, fslib_1.Filename.nodeModules);
120
- if (dependencies.length > 0)
121
- await fslib_1.xfs.mkdirpPromise(nmPath);
132
+ const concurrentPromises = [];
122
133
  // Retrieve what's currently inside the package's true nm folder. We
123
134
  // will use that to figure out what are the extraneous entries we'll
124
135
  // need to remove.
125
136
  const extraneous = await getNodeModulesListing(nmPath);
126
- const concurrentPromises = [];
127
137
  for (const [descriptor, dependency] of dependencies) {
128
138
  // Downgrade virtual workspaces (cf isPnpmVirtualCompatible's documentation)
129
139
  let targetDependency = dependency;
@@ -131,7 +141,7 @@ class PnpmInstaller {
131
141
  this.opts.report.reportWarning(core_1.MessageName.UNNAMED, `The pnpm linker doesn't support providing different versions to workspaces' peer dependencies`);
132
142
  targetDependency = core_1.structUtils.devirtualizeLocator(dependency);
133
143
  }
134
- const depSrcPath = this.packageLocations.get(targetDependency.locatorHash);
144
+ const depSrcPath = this.customData.pathByLocator.get(targetDependency.locatorHash);
135
145
  if (typeof depSrcPath === `undefined`)
136
146
  throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(dependency)})`);
137
147
  const name = core_1.structUtils.stringifyIdent(descriptor);
@@ -158,8 +168,7 @@ class PnpmInstaller {
158
168
  }
159
169
  }));
160
170
  }
161
- for (const name of extraneous.keys())
162
- concurrentPromises.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(nmPath, name)));
171
+ concurrentPromises.push(cleanNodeModules(nmPath, extraneous));
163
172
  await Promise.all(concurrentPromises);
164
173
  });
165
174
  }
@@ -168,37 +177,76 @@ class PnpmInstaller {
168
177
  }
169
178
  async finalizeInstall() {
170
179
  const storeLocation = getStoreLocation(this.opts.project);
171
- const expectedEntries = new Set();
172
- for (const packageLocation of this.packageLocations.values())
173
- expectedEntries.add(fslib_1.ppath.basename(packageLocation));
174
- let storeRecords;
175
- try {
176
- storeRecords = await fslib_1.xfs.readdirPromise(storeLocation);
180
+ if (this.opts.project.configuration.get(`nodeLinker`) !== `pnpm`) {
181
+ await fslib_1.xfs.removePromise(storeLocation);
177
182
  }
178
- catch {
179
- storeRecords = [];
183
+ else {
184
+ const removals = [];
185
+ const expectedEntries = new Set();
186
+ for (const packageLocation of this.customData.pathByLocator.values()) {
187
+ const subpath = fslib_1.ppath.contains(storeLocation, packageLocation);
188
+ if (subpath !== null) {
189
+ const [storeEntry, /* Filename.nodeModules */ , ...identComponents] = subpath.split(fslib_1.ppath.sep);
190
+ expectedEntries.add(storeEntry);
191
+ const storeEntryPath = fslib_1.ppath.join(storeLocation, storeEntry);
192
+ removals.push(fslib_1.xfs.readdirPromise(storeEntryPath)
193
+ .then(entries => {
194
+ return Promise.all(entries.map(async (entry) => {
195
+ const p = fslib_1.ppath.join(storeEntryPath, entry);
196
+ if (entry === fslib_1.Filename.nodeModules) {
197
+ const extraneous = await getNodeModulesListing(p);
198
+ extraneous.delete(identComponents.join(fslib_1.ppath.sep));
199
+ return cleanNodeModules(p, extraneous);
200
+ }
201
+ else {
202
+ return fslib_1.xfs.removePromise(p);
203
+ }
204
+ }));
205
+ })
206
+ .catch(error => {
207
+ if (error.code !== `ENOENT`) {
208
+ throw error;
209
+ }
210
+ }));
211
+ }
212
+ }
213
+ let storeRecords;
214
+ try {
215
+ storeRecords = await fslib_1.xfs.readdirPromise(storeLocation);
216
+ }
217
+ catch {
218
+ storeRecords = [];
219
+ }
220
+ for (const record of storeRecords)
221
+ if (!expectedEntries.has(record))
222
+ removals.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(storeLocation, record)));
223
+ await Promise.all(removals);
180
224
  }
181
- const removals = [];
182
- for (const record of storeRecords)
183
- if (!expectedEntries.has(record))
184
- removals.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(storeLocation, record)));
185
- await Promise.all(removals);
186
225
  // Wait for the package installs to catch up
187
- await this.asyncActions.wait();
226
+ await this.asyncActions.wait(),
227
+ await removeIfEmpty(storeLocation);
228
+ await removeIfEmpty(getNodeModulesLocation(this.opts.project));
229
+ return {
230
+ customData: this.customData,
231
+ };
188
232
  }
189
233
  }
190
234
  function getCustomDataKey() {
191
235
  return JSON.stringify({
192
236
  name: `PnpmInstaller`,
193
- version: 1,
237
+ version: 2,
194
238
  });
195
239
  }
240
+ function getNodeModulesLocation(project) {
241
+ return fslib_1.ppath.join(project.cwd, fslib_1.Filename.nodeModules);
242
+ }
196
243
  function getStoreLocation(project) {
197
- return fslib_1.ppath.join(project.cwd, fslib_1.Filename.nodeModules, `.store`);
244
+ return fslib_1.ppath.join(getNodeModulesLocation(project), `.store`);
198
245
  }
199
246
  function getPackageLocation(locator, { project }) {
200
247
  const pkgKey = core_1.structUtils.slugifyLocator(locator);
201
- const pkgPath = fslib_1.ppath.join(getStoreLocation(project), pkgKey);
248
+ const prefixPath = core_1.structUtils.getIdentVendorPath(locator);
249
+ const pkgPath = fslib_1.ppath.join(getStoreLocation(project), pkgKey, prefixPath);
202
250
  return pkgPath;
203
251
  }
204
252
  function isPnpmVirtualCompatible(locator, { project }) {
@@ -231,8 +279,14 @@ async function getNodeModulesListing(nmPath) {
231
279
  if (entry.name.startsWith(`.`))
232
280
  continue;
233
281
  if (entry.name.startsWith(`@`)) {
234
- for (const subEntry of await fslib_1.xfs.readdirPromise(fslib_1.ppath.join(nmPath, entry.name), { withFileTypes: true })) {
235
- listing.set(`${entry.name}/${subEntry.name}`, subEntry);
282
+ const scopeListing = await fslib_1.xfs.readdirPromise(fslib_1.ppath.join(nmPath, entry.name), { withFileTypes: true });
283
+ if (scopeListing.length === 0) {
284
+ listing.set(entry.name, entry);
285
+ }
286
+ else {
287
+ for (const subEntry of scopeListing) {
288
+ listing.set(`${entry.name}/${subEntry.name}`, subEntry);
289
+ }
236
290
  }
237
291
  }
238
292
  else {
@@ -247,44 +301,26 @@ async function getNodeModulesListing(nmPath) {
247
301
  }
248
302
  return listing;
249
303
  }
250
- function makeDeferred() {
251
- let resolve;
252
- let reject;
253
- const promise = new Promise((resolveFn, rejectFn) => {
254
- resolve = resolveFn;
255
- reject = rejectFn;
256
- });
257
- return { promise, resolve: resolve, reject: reject };
258
- }
259
- class AsyncActions {
260
- constructor() {
261
- this.deferred = new Map();
262
- this.promises = new Map();
263
- this.limit = (0, p_limit_1.default)(10);
264
- }
265
- set(key, factory) {
266
- let deferred = this.deferred.get(key);
267
- if (typeof deferred === `undefined`)
268
- this.deferred.set(key, deferred = makeDeferred());
269
- const promise = this.limit(() => factory());
270
- this.promises.set(key, promise);
271
- promise.then(() => {
272
- if (this.promises.get(key) === promise) {
273
- deferred.resolve();
274
- }
275
- }, err => {
276
- if (this.promises.get(key) === promise) {
277
- deferred.reject(err);
278
- }
279
- });
280
- return deferred.promise;
304
+ async function cleanNodeModules(nmPath, extraneous) {
305
+ var _a;
306
+ const removeNamePromises = [];
307
+ const scopesToRemove = new Set();
308
+ for (const name of extraneous.keys()) {
309
+ removeNamePromises.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(nmPath, name)));
310
+ const scope = (_a = core_1.structUtils.tryParseIdent(name)) === null || _a === void 0 ? void 0 : _a.scope;
311
+ if (scope) {
312
+ scopesToRemove.add(`@${scope}`);
313
+ }
281
314
  }
282
- reduce(key, factory) {
283
- var _a;
284
- const promise = (_a = this.promises.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
285
- this.set(key, () => factory(promise));
315
+ return Promise.all(removeNamePromises).then(() => Promise.all([...scopesToRemove].map(scope => removeIfEmpty(fslib_1.ppath.join(nmPath, scope)))));
316
+ }
317
+ async function removeIfEmpty(dir) {
318
+ try {
319
+ await fslib_1.xfs.rmdirPromise(dir);
286
320
  }
287
- async wait() {
288
- await Promise.all(this.promises.values());
321
+ catch (error) {
322
+ if (error.code !== `ENOENT` && error.code !== `ENOTEMPTY`) {
323
+ throw error;
324
+ }
289
325
  }
290
326
  }
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@yarnpkg/plugin-pnpm",
3
- "version": "1.0.1",
3
+ "version": "1.1.0-rc.11",
4
4
  "license": "BSD-2-Clause",
5
5
  "main": "./lib/index.js",
6
6
  "dependencies": {
7
- "@yarnpkg/fslib": "^2.6.0",
8
- "@yarnpkg/plugin-pnp": "^3.1.1",
7
+ "@yarnpkg/fslib": "^2.6.1-rc.8",
8
+ "@yarnpkg/plugin-pnp": "^3.2.0-rc.3",
9
9
  "@yarnpkg/plugin-stage": "^3.1.1",
10
- "clipanion": "^3.0.1",
10
+ "clipanion": "^3.2.0-rc.4",
11
11
  "p-limit": "^2.2.0",
12
12
  "tslib": "^1.13.0"
13
13
  },
14
14
  "peerDependencies": {
15
- "@yarnpkg/cli": "^3.1.0",
16
- "@yarnpkg/core": "^3.1.0"
15
+ "@yarnpkg/cli": "^3.2.0-rc.13",
16
+ "@yarnpkg/core": "^3.2.0-rc.13"
17
17
  },
18
18
  "devDependencies": {
19
- "@yarnpkg/cli": "^3.1.0",
20
- "@yarnpkg/core": "^3.1.0"
19
+ "@yarnpkg/cli": "^3.2.0-rc.13",
20
+ "@yarnpkg/core": "^3.2.0-rc.13"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
@@ -38,5 +38,6 @@
38
38
  "engines": {
39
39
  "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0"
40
40
  },
41
+ "stableVersion": "1.0.1",
41
42
  "typings": "./lib/index.d.ts"
42
43
  }