@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.
- package/lib/PnpmLinker.d.ts +8 -5
- package/lib/PnpmLinker.js +83 -67
- package/package.json +8 -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
|
}
|
|
@@ -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.
|
|
177
|
-
|
|
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(
|
|
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:
|
|
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
|
|
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
|
-
|
|
245
|
-
|
|
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
|
|
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.
|
|
8
|
-
"@yarnpkg/plugin-pnp": "^3.
|
|
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.
|
|
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.
|
|
16
|
-
"@yarnpkg/core": "^3.2.0-rc.
|
|
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.
|
|
20
|
-
"@yarnpkg/core": "^3.2.0-rc.
|
|
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",
|