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

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,7 +1,8 @@
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;
@@ -12,7 +13,6 @@ export declare class PnpmLinker implements Linker {
12
13
  declare class PnpmInstaller implements Installer {
13
14
  private opts;
14
15
  private asyncActions;
15
- private packageLocations;
16
16
  constructor(opts: LinkOptions);
17
17
  getCustomDataKey(): string;
18
18
  private customData;
@@ -34,6 +34,8 @@ declare class PnpmInstaller implements Installer {
34
34
  }>;
35
35
  attachInternalDependencies(locator: Locator, dependencies: Array<[Descriptor, Locator]>): Promise<void>;
36
36
  attachExternalDependents(locator: Locator, dependentPaths: Array<PortablePath>): Promise<void>;
37
- finalizeInstall(): Promise<void>;
37
+ finalizeInstall(): Promise<{
38
+ customData: PnpmCustomData;
39
+ }>;
38
40
  }
39
41
  export {};
package/lib/PnpmLinker.js CHANGED
@@ -12,8 +12,14 @@ class PnpmLinker {
12
12
  return opts.project.configuration.get(`nodeLinker`) === `pnpm`;
13
13
  }
14
14
  async findPackageLocation(locator, opts) {
15
- // TODO: Support soft linked packages
16
- return getPackageLocation(locator, { project: opts.project });
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) {
19
25
  const customDataKey = getCustomDataKey();
@@ -48,8 +54,8 @@ class PnpmInstaller {
48
54
  constructor(opts) {
49
55
  this.opts = opts;
50
56
  this.asyncActions = new AsyncActions();
51
- this.packageLocations = new Map();
52
57
  this.customData = {
58
+ pathByLocator: new Map(),
53
59
  locatorByPath: new Map(),
54
60
  };
55
61
  // Nothing to do
@@ -58,7 +64,8 @@ class PnpmInstaller {
58
64
  return getCustomDataKey();
59
65
  }
60
66
  attachCustomData(customData) {
61
- this.customData = customData;
67
+ // 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,
68
+ // it needs to be invalidated because otherwise we'll never prune the store or we might run into various issues.
62
69
  }
63
70
  async installPackage(pkg, fetchResult, api) {
64
71
  switch (pkg.linkType) {
@@ -69,7 +76,7 @@ class PnpmInstaller {
69
76
  }
70
77
  async installPackageSoft(pkg, fetchResult, api) {
71
78
  const pkgPath = fslib_1.ppath.resolve(fetchResult.packageFs.getRealPath(), fetchResult.prefixPath);
72
- this.packageLocations.set(pkg.locatorHash, pkgPath);
79
+ this.customData.pathByLocator.set(pkg.locatorHash, pkgPath);
73
80
  return {
74
81
  packageLocation: pkgPath,
75
82
  buildDirective: null,
@@ -79,7 +86,7 @@ class PnpmInstaller {
79
86
  var _a;
80
87
  const pkgPath = getPackageLocation(pkg, { project: this.opts.project });
81
88
  this.customData.locatorByPath.set(pkgPath, core_1.structUtils.stringifyLocator(pkg));
82
- this.packageLocations.set(pkg.locatorHash, pkgPath);
89
+ this.customData.pathByLocator.set(pkg.locatorHash, pkgPath);
83
90
  api.holdFetchResult(this.asyncActions.set(pkg.locatorHash, async () => {
84
91
  await fslib_1.xfs.mkdirPromise(pkgPath, { recursive: true });
85
92
  // Copy the package source into the <root>/n_m/.store/<hash> directory, so
@@ -113,17 +120,15 @@ class PnpmInstaller {
113
120
  this.asyncActions.reduce(locator.locatorHash, async (action) => {
114
121
  // Wait that the package is properly installed before starting to copy things into it
115
122
  await action;
116
- const pkgPath = this.packageLocations.get(locator.locatorHash);
123
+ const pkgPath = this.customData.pathByLocator.get(locator.locatorHash);
117
124
  if (typeof pkgPath === `undefined`)
118
125
  throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(locator)})`);
119
126
  const nmPath = fslib_1.ppath.join(pkgPath, fslib_1.Filename.nodeModules);
120
- if (dependencies.length > 0)
121
- await fslib_1.xfs.mkdirpPromise(nmPath);
127
+ const concurrentPromises = [];
122
128
  // Retrieve what's currently inside the package's true nm folder. We
123
129
  // will use that to figure out what are the extraneous entries we'll
124
130
  // need to remove.
125
131
  const extraneous = await getNodeModulesListing(nmPath);
126
- const concurrentPromises = [];
127
132
  for (const [descriptor, dependency] of dependencies) {
128
133
  // Downgrade virtual workspaces (cf isPnpmVirtualCompatible's documentation)
129
134
  let targetDependency = dependency;
@@ -131,7 +136,7 @@ class PnpmInstaller {
131
136
  this.opts.report.reportWarning(core_1.MessageName.UNNAMED, `The pnpm linker doesn't support providing different versions to workspaces' peer dependencies`);
132
137
  targetDependency = core_1.structUtils.devirtualizeLocator(dependency);
133
138
  }
134
- const depSrcPath = this.packageLocations.get(targetDependency.locatorHash);
139
+ const depSrcPath = this.customData.pathByLocator.get(targetDependency.locatorHash);
135
140
  if (typeof depSrcPath === `undefined`)
136
141
  throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(dependency)})`);
137
142
  const name = core_1.structUtils.stringifyIdent(descriptor);
@@ -158,8 +163,7 @@ class PnpmInstaller {
158
163
  }
159
164
  }));
160
165
  }
161
- for (const name of extraneous.keys())
162
- concurrentPromises.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(nmPath, name)));
166
+ concurrentPromises.push(cleanNodeModules(nmPath, extraneous));
163
167
  await Promise.all(concurrentPromises);
164
168
  });
165
169
  }
@@ -172,9 +176,35 @@ class PnpmInstaller {
172
176
  await fslib_1.xfs.removePromise(storeLocation);
173
177
  }
174
178
  else {
179
+ const removals = [];
175
180
  const expectedEntries = new Set();
176
- for (const packageLocation of this.packageLocations.values())
177
- expectedEntries.add(fslib_1.ppath.basename(packageLocation));
181
+ for (const packageLocation of this.customData.pathByLocator.values()) {
182
+ const subpath = fslib_1.ppath.contains(storeLocation, packageLocation);
183
+ if (subpath !== null) {
184
+ const [storeEntry, /* Filename.nodeModules */ , ...identComponents] = subpath.split(fslib_1.ppath.sep);
185
+ expectedEntries.add(storeEntry);
186
+ const storeEntryPath = fslib_1.ppath.join(storeLocation, storeEntry);
187
+ removals.push(fslib_1.xfs.readdirPromise(storeEntryPath)
188
+ .then(entries => {
189
+ return Promise.all(entries.map(async (entry) => {
190
+ const p = fslib_1.ppath.join(storeEntryPath, entry);
191
+ if (entry === fslib_1.Filename.nodeModules) {
192
+ const extraneous = await getNodeModulesListing(p);
193
+ extraneous.delete(identComponents.join(fslib_1.ppath.sep));
194
+ return cleanNodeModules(p, extraneous);
195
+ }
196
+ else {
197
+ return fslib_1.xfs.removePromise(p);
198
+ }
199
+ }));
200
+ })
201
+ .catch(error => {
202
+ if (error.code !== `ENOENT`) {
203
+ throw error;
204
+ }
205
+ }));
206
+ }
207
+ }
178
208
  let storeRecords;
179
209
  try {
180
210
  storeRecords = await fslib_1.xfs.readdirPromise(storeLocation);
@@ -182,7 +212,6 @@ class PnpmInstaller {
182
212
  catch {
183
213
  storeRecords = [];
184
214
  }
185
- const removals = [];
186
215
  for (const record of storeRecords)
187
216
  if (!expectedEntries.has(record))
188
217
  removals.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(storeLocation, record)));
@@ -192,12 +221,15 @@ class PnpmInstaller {
192
221
  // Wait for the package installs to catch up
193
222
  await this.asyncActions.wait(),
194
223
  await removeIfEmpty(getNodeModulesLocation(this.opts.project));
224
+ return {
225
+ customData: this.customData,
226
+ };
195
227
  }
196
228
  }
197
229
  function getCustomDataKey() {
198
230
  return JSON.stringify({
199
231
  name: `PnpmInstaller`,
200
- version: 1,
232
+ version: 2,
201
233
  });
202
234
  }
203
235
  function getNodeModulesLocation(project) {
@@ -208,7 +240,8 @@ function getStoreLocation(project) {
208
240
  }
209
241
  function getPackageLocation(locator, { project }) {
210
242
  const pkgKey = core_1.structUtils.slugifyLocator(locator);
211
- const pkgPath = fslib_1.ppath.join(getStoreLocation(project), pkgKey);
243
+ const prefixPath = core_1.structUtils.getIdentVendorPath(locator);
244
+ const pkgPath = fslib_1.ppath.join(getStoreLocation(project), pkgKey, prefixPath);
212
245
  return pkgPath;
213
246
  }
214
247
  function isPnpmVirtualCompatible(locator, { project }) {
@@ -241,8 +274,14 @@ async function getNodeModulesListing(nmPath) {
241
274
  if (entry.name.startsWith(`.`))
242
275
  continue;
243
276
  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);
277
+ const scopeListing = await fslib_1.xfs.readdirPromise(fslib_1.ppath.join(nmPath, entry.name), { withFileTypes: true });
278
+ if (scopeListing.length === 0) {
279
+ listing.set(entry.name, entry);
280
+ }
281
+ else {
282
+ for (const subEntry of scopeListing) {
283
+ listing.set(`${entry.name}/${subEntry.name}`, subEntry);
284
+ }
246
285
  }
247
286
  }
248
287
  else {
@@ -257,6 +296,19 @@ async function getNodeModulesListing(nmPath) {
257
296
  }
258
297
  return listing;
259
298
  }
299
+ async function cleanNodeModules(nmPath, extraneous) {
300
+ var _a;
301
+ const removeNamePromises = [];
302
+ const scopesToRemove = new Set();
303
+ for (const name of extraneous.keys()) {
304
+ removeNamePromises.push(fslib_1.xfs.removePromise(fslib_1.ppath.join(nmPath, name)));
305
+ const scope = (_a = core_1.structUtils.tryParseIdent(name)) === null || _a === void 0 ? void 0 : _a.scope;
306
+ if (scope) {
307
+ scopesToRemove.add(`@${scope}`);
308
+ }
309
+ }
310
+ return Promise.all(removeNamePromises).then(() => Promise.all([...scopesToRemove].map(scope => removeIfEmpty(fslib_1.ppath.join(nmPath, scope)))));
311
+ }
260
312
  async function removeIfEmpty(dir) {
261
313
  try {
262
314
  await fslib_1.xfs.rmdirPromise(dir);
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.4",
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.1",
8
+ "@yarnpkg/plugin-pnp": "^3.1.2-rc.6",
9
9
  "@yarnpkg/plugin-stage": "^3.1.1",
10
10
  "clipanion": "^3.0.1",
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.6",
16
+ "@yarnpkg/core": "^3.2.0-rc.6"
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.6",
20
+ "@yarnpkg/core": "^3.2.0-rc.6"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",