@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.
- package/lib/PnpmLinker.d.ts +8 -5
- package/lib/PnpmLinker.js +109 -73
- package/package.json +9 -8
package/lib/PnpmLinker.d.ts
CHANGED
|
@@ -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
|
-
|
|
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<
|
|
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
|
|
10
|
+
return this.isEnabled(opts);
|
|
13
11
|
}
|
|
14
12
|
async findPackageLocation(locator, opts) {
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
235
|
-
|
|
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
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
288
|
-
|
|
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.
|
|
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.
|
|
8
|
-
"@yarnpkg/plugin-pnp": "^3.
|
|
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.
|
|
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.
|
|
16
|
-
"@yarnpkg/core": "^3.
|
|
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.
|
|
20
|
-
"@yarnpkg/core": "^3.
|
|
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
|
}
|