@yarnpkg/plugin-pnpm 1.0.2-rc.1 → 1.1.0-rc.12

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
  }
@@ -172,9 +181,35 @@ class PnpmInstaller {
172
181
  await fslib_1.xfs.removePromise(storeLocation);
173
182
  }
174
183
  else {
184
+ const removals = [];
175
185
  const expectedEntries = new Set();
176
- for (const packageLocation of this.packageLocations.values())
177
- expectedEntries.add(fslib_1.ppath.basename(packageLocation));
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
+ }
178
213
  let storeRecords;
179
214
  try {
180
215
  storeRecords = await fslib_1.xfs.readdirPromise(storeLocation);
@@ -182,22 +217,24 @@ class PnpmInstaller {
182
217
  catch {
183
218
  storeRecords = [];
184
219
  }
185
- const removals = [];
186
220
  for (const record of storeRecords)
187
221
  if (!expectedEntries.has(record))
188
222
  removals.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(storeLocation, record)));
189
223
  await Promise.all(removals);
190
- await removeIfEmpty(storeLocation);
191
224
  }
192
225
  // Wait for the package installs to catch up
193
226
  await this.asyncActions.wait(),
194
- await removeIfEmpty(getNodeModulesLocation(this.opts.project));
227
+ await removeIfEmpty(storeLocation);
228
+ await removeIfEmpty(getNodeModulesLocation(this.opts.project));
229
+ return {
230
+ customData: this.customData,
231
+ };
195
232
  }
196
233
  }
197
234
  function getCustomDataKey() {
198
235
  return JSON.stringify({
199
236
  name: `PnpmInstaller`,
200
- version: 1,
237
+ version: 2,
201
238
  });
202
239
  }
203
240
  function getNodeModulesLocation(project) {
@@ -208,7 +245,8 @@ function getStoreLocation(project) {
208
245
  }
209
246
  function getPackageLocation(locator, { project }) {
210
247
  const pkgKey = core_1.structUtils.slugifyLocator(locator);
211
- 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);
212
250
  return pkgPath;
213
251
  }
214
252
  function isPnpmVirtualCompatible(locator, { project }) {
@@ -241,8 +279,14 @@ async function getNodeModulesListing(nmPath) {
241
279
  if (entry.name.startsWith(`.`))
242
280
  continue;
243
281
  if (entry.name.startsWith(`@`)) {
244
- for (const subEntry of await fslib_1.xfs.readdirPromise(fslib_1.ppath.join(nmPath, entry.name), { withFileTypes: true })) {
245
- 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
+ }
246
290
  }
247
291
  }
248
292
  else {
@@ -257,6 +301,19 @@ async function getNodeModulesListing(nmPath) {
257
301
  }
258
302
  return listing;
259
303
  }
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
+ }
314
+ }
315
+ return Promise.all(removeNamePromises).then(() => Promise.all([...scopesToRemove].map(scope => removeIfEmpty(fslib_1.ppath.join(nmPath, scope)))));
316
+ }
260
317
  async function removeIfEmpty(dir) {
261
318
  try {
262
319
  await fslib_1.xfs.rmdirPromise(dir);
@@ -267,44 +324,3 @@ async function removeIfEmpty(dir) {
267
324
  }
268
325
  }
269
326
  }
270
- function makeDeferred() {
271
- let resolve;
272
- let reject;
273
- const promise = new Promise((resolveFn, rejectFn) => {
274
- resolve = resolveFn;
275
- reject = rejectFn;
276
- });
277
- return { promise, resolve: resolve, reject: reject };
278
- }
279
- class AsyncActions {
280
- constructor() {
281
- this.deferred = new Map();
282
- this.promises = new Map();
283
- this.limit = (0, p_limit_1.default)(10);
284
- }
285
- set(key, factory) {
286
- let deferred = this.deferred.get(key);
287
- if (typeof deferred === `undefined`)
288
- this.deferred.set(key, deferred = makeDeferred());
289
- const promise = this.limit(() => factory());
290
- this.promises.set(key, promise);
291
- promise.then(() => {
292
- if (this.promises.get(key) === promise) {
293
- deferred.resolve();
294
- }
295
- }, err => {
296
- if (this.promises.get(key) === promise) {
297
- deferred.reject(err);
298
- }
299
- });
300
- return deferred.promise;
301
- }
302
- reduce(key, factory) {
303
- var _a;
304
- const promise = (_a = this.promises.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
305
- this.set(key, () => factory(promise));
306
- }
307
- async wait() {
308
- await Promise.all(this.promises.values());
309
- }
310
- }
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@yarnpkg/plugin-pnpm",
3
- "version": "1.0.2-rc.1",
3
+ "version": "1.1.0-rc.12",
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.2-rc.2",
7
+ "@yarnpkg/fslib": "^2.6.1-rc.9",
8
+ "@yarnpkg/plugin-pnp": "^3.2.0-rc.4",
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.2.0-rc.2",
16
- "@yarnpkg/core": "^3.2.0-rc.2"
15
+ "@yarnpkg/cli": "^3.2.0-rc.14",
16
+ "@yarnpkg/core": "^3.2.0-rc.14"
17
17
  },
18
18
  "devDependencies": {
19
- "@yarnpkg/cli": "^3.2.0-rc.2",
20
- "@yarnpkg/core": "^3.2.0-rc.2"
19
+ "@yarnpkg/cli": "^3.2.0-rc.14",
20
+ "@yarnpkg/core": "^3.2.0-rc.14"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",