@yarnpkg/plugin-pnpm 2.0.0-rc.3 → 2.0.0-rc.30
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 +7 -3
- package/lib/PnpmLinker.js +85 -84
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/package.json +18 -12
package/lib/PnpmLinker.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Descriptor, FetchResult, Installer, InstallPackageExtraApi, Linker, LinkOptions, Locator, LocatorHash, MinimalLinkOptions, Package } from '@yarnpkg/core';
|
|
2
2
|
import { PortablePath } from '@yarnpkg/fslib';
|
|
3
|
-
export
|
|
4
|
-
pathByLocator: Map<LocatorHash, PortablePath>;
|
|
3
|
+
export type PnpmCustomData = {
|
|
5
4
|
locatorByPath: Map<PortablePath, string>;
|
|
5
|
+
pathsByLocator: Map<LocatorHash, {
|
|
6
|
+
packageLocation: PortablePath;
|
|
7
|
+
dependenciesLocation: PortablePath | null;
|
|
8
|
+
}>;
|
|
6
9
|
};
|
|
7
10
|
export declare class PnpmLinker implements Linker {
|
|
11
|
+
getCustomDataKey(): string;
|
|
8
12
|
supportsPackage(pkg: Package, opts: MinimalLinkOptions): boolean;
|
|
9
13
|
findPackageLocation(locator: Locator, opts: LinkOptions): Promise<PortablePath>;
|
|
10
14
|
findPackageLocator(location: PortablePath, opts: LinkOptions): Promise<Locator | null>;
|
|
@@ -14,8 +18,8 @@ export declare class PnpmLinker implements Linker {
|
|
|
14
18
|
declare class PnpmInstaller implements Installer {
|
|
15
19
|
private opts;
|
|
16
20
|
private readonly asyncActions;
|
|
21
|
+
private readonly indexFolderPromise;
|
|
17
22
|
constructor(opts: LinkOptions);
|
|
18
|
-
getCustomDataKey(): string;
|
|
19
23
|
private customData;
|
|
20
24
|
attachCustomData(customData: any): void;
|
|
21
25
|
installPackage(pkg: Package, fetchResult: FetchResult, api: InstallPackageExtraApi): Promise<{
|
package/lib/PnpmLinker.js
CHANGED
|
@@ -6,26 +6,32 @@ const fslib_1 = require("@yarnpkg/fslib");
|
|
|
6
6
|
const plugin_pnp_1 = require("@yarnpkg/plugin-pnp");
|
|
7
7
|
const clipanion_1 = require("clipanion");
|
|
8
8
|
class PnpmLinker {
|
|
9
|
+
getCustomDataKey() {
|
|
10
|
+
return JSON.stringify({
|
|
11
|
+
name: `PnpmLinker`,
|
|
12
|
+
version: 3,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
9
15
|
supportsPackage(pkg, opts) {
|
|
10
16
|
return this.isEnabled(opts);
|
|
11
17
|
}
|
|
12
18
|
async findPackageLocation(locator, opts) {
|
|
13
19
|
if (!this.isEnabled(opts))
|
|
14
20
|
throw new Error(`Assertion failed: Expected the pnpm linker to be enabled`);
|
|
15
|
-
const customDataKey = getCustomDataKey();
|
|
16
|
-
const customData = opts.project.
|
|
21
|
+
const customDataKey = this.getCustomDataKey();
|
|
22
|
+
const customData = opts.project.linkersCustomData.get(customDataKey);
|
|
17
23
|
if (!customData)
|
|
18
24
|
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
|
|
20
|
-
if (typeof
|
|
25
|
+
const packagePaths = customData.pathsByLocator.get(locator.locatorHash);
|
|
26
|
+
if (typeof packagePaths === `undefined`)
|
|
21
27
|
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;
|
|
28
|
+
return packagePaths.packageLocation;
|
|
23
29
|
}
|
|
24
30
|
async findPackageLocator(location, opts) {
|
|
25
31
|
if (!this.isEnabled(opts))
|
|
26
32
|
return null;
|
|
27
|
-
const customDataKey = getCustomDataKey();
|
|
28
|
-
const customData = opts.project.
|
|
33
|
+
const customDataKey = this.getCustomDataKey();
|
|
34
|
+
const customData = opts.project.linkersCustomData.get(customDataKey);
|
|
29
35
|
if (!customData)
|
|
30
36
|
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`);
|
|
31
37
|
const nmRootLocation = location.match(/(^.*\/node_modules\/(@[^/]*\/)?[^/]+)(\/.*$)/);
|
|
@@ -60,13 +66,12 @@ class PnpmInstaller {
|
|
|
60
66
|
this.opts = opts;
|
|
61
67
|
this.asyncActions = new core_1.miscUtils.AsyncActions(10);
|
|
62
68
|
this.customData = {
|
|
63
|
-
|
|
69
|
+
pathsByLocator: new Map(),
|
|
64
70
|
locatorByPath: new Map(),
|
|
65
71
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return getCustomDataKey();
|
|
72
|
+
this.indexFolderPromise = (0, fslib_1.setupCopyIndex)(fslib_1.xfs, {
|
|
73
|
+
indexPath: fslib_1.ppath.join(opts.project.configuration.get(`globalFolder`), `index`),
|
|
74
|
+
});
|
|
70
75
|
}
|
|
71
76
|
attachCustomData(customData) {
|
|
72
77
|
// 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,
|
|
@@ -80,25 +85,37 @@ class PnpmInstaller {
|
|
|
80
85
|
throw new Error(`Assertion failed: Unsupported package link type`);
|
|
81
86
|
}
|
|
82
87
|
async installPackageSoft(pkg, fetchResult, api) {
|
|
83
|
-
const
|
|
84
|
-
this.
|
|
88
|
+
const packageLocation = fslib_1.ppath.resolve(fetchResult.packageFs.getRealPath(), fetchResult.prefixPath);
|
|
89
|
+
const dependenciesLocation = this.opts.project.tryWorkspaceByLocator(pkg)
|
|
90
|
+
? fslib_1.ppath.join(packageLocation, fslib_1.Filename.nodeModules)
|
|
91
|
+
: null;
|
|
92
|
+
this.customData.pathsByLocator.set(pkg.locatorHash, {
|
|
93
|
+
packageLocation,
|
|
94
|
+
dependenciesLocation,
|
|
95
|
+
});
|
|
85
96
|
return {
|
|
86
|
-
packageLocation
|
|
97
|
+
packageLocation,
|
|
87
98
|
buildDirective: null,
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
101
|
async installPackageHard(pkg, fetchResult, api) {
|
|
91
102
|
var _a;
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
this.customData.
|
|
103
|
+
const packagePaths = getPackagePaths(pkg, { project: this.opts.project });
|
|
104
|
+
const packageLocation = packagePaths.packageLocation;
|
|
105
|
+
this.customData.locatorByPath.set(packageLocation, core_1.structUtils.stringifyLocator(pkg));
|
|
106
|
+
this.customData.pathsByLocator.set(pkg.locatorHash, packagePaths);
|
|
95
107
|
api.holdFetchResult(this.asyncActions.set(pkg.locatorHash, async () => {
|
|
96
|
-
await fslib_1.xfs.mkdirPromise(
|
|
108
|
+
await fslib_1.xfs.mkdirPromise(packageLocation, { recursive: true });
|
|
97
109
|
// Copy the package source into the <root>/n_m/.store/<hash> directory, so
|
|
98
110
|
// that we can then create symbolic links to it later.
|
|
99
|
-
await fslib_1.xfs.copyPromise(
|
|
111
|
+
await fslib_1.xfs.copyPromise(packageLocation, fetchResult.prefixPath, {
|
|
100
112
|
baseFs: fetchResult.packageFs,
|
|
101
113
|
overwrite: false,
|
|
114
|
+
linkStrategy: {
|
|
115
|
+
type: `HardlinkFromIndex`,
|
|
116
|
+
indexPath: await this.indexFolderPromise,
|
|
117
|
+
autoRepair: true,
|
|
118
|
+
},
|
|
102
119
|
});
|
|
103
120
|
}));
|
|
104
121
|
const isVirtual = core_1.structUtils.isVirtualLocator(pkg);
|
|
@@ -112,7 +129,7 @@ class PnpmInstaller {
|
|
|
112
129
|
const dependencyMeta = this.opts.project.getDependencyMeta(devirtualizedLocator, pkg.version);
|
|
113
130
|
const buildScripts = plugin_pnp_1.jsInstallUtils.extractBuildScripts(pkg, buildConfig, dependencyMeta, { configuration: this.opts.project.configuration, report: this.opts.report });
|
|
114
131
|
return {
|
|
115
|
-
packageLocation
|
|
132
|
+
packageLocation,
|
|
116
133
|
buildDirective: buildScripts,
|
|
117
134
|
};
|
|
118
135
|
}
|
|
@@ -122,31 +139,33 @@ class PnpmInstaller {
|
|
|
122
139
|
// We don't install those packages at all, because they can't be used anyway
|
|
123
140
|
if (!isPnpmVirtualCompatible(locator, { project: this.opts.project }))
|
|
124
141
|
return;
|
|
142
|
+
const packagePaths = this.customData.pathsByLocator.get(locator.locatorHash);
|
|
143
|
+
if (typeof packagePaths === `undefined`)
|
|
144
|
+
throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(locator)})`);
|
|
145
|
+
const { dependenciesLocation, } = packagePaths;
|
|
146
|
+
if (!dependenciesLocation)
|
|
147
|
+
return;
|
|
125
148
|
this.asyncActions.reduce(locator.locatorHash, async (action) => {
|
|
126
|
-
|
|
127
|
-
await action;
|
|
128
|
-
const pkgPath = this.customData.pathByLocator.get(locator.locatorHash);
|
|
129
|
-
if (typeof pkgPath === `undefined`)
|
|
130
|
-
throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(locator)})`);
|
|
131
|
-
const nmPath = fslib_1.ppath.join(pkgPath, fslib_1.Filename.nodeModules);
|
|
132
|
-
const concurrentPromises = [];
|
|
149
|
+
await fslib_1.xfs.mkdirPromise(dependenciesLocation, { recursive: true });
|
|
133
150
|
// Retrieve what's currently inside the package's true nm folder. We
|
|
134
151
|
// will use that to figure out what are the extraneous entries we'll
|
|
135
152
|
// need to remove.
|
|
136
|
-
const
|
|
137
|
-
|
|
153
|
+
const initialEntries = await getNodeModulesListing(dependenciesLocation);
|
|
154
|
+
const extraneous = new Map(initialEntries);
|
|
155
|
+
const concurrentPromises = [action];
|
|
156
|
+
const installDependency = (descriptor, dependency) => {
|
|
138
157
|
// Downgrade virtual workspaces (cf isPnpmVirtualCompatible's documentation)
|
|
139
158
|
let targetDependency = dependency;
|
|
140
159
|
if (!isPnpmVirtualCompatible(dependency, { project: this.opts.project })) {
|
|
141
|
-
this.opts.report.
|
|
160
|
+
this.opts.report.reportWarningOnce(core_1.MessageName.UNNAMED, `The pnpm linker doesn't support providing different versions to workspaces' peer dependencies`);
|
|
142
161
|
targetDependency = core_1.structUtils.devirtualizeLocator(dependency);
|
|
143
162
|
}
|
|
144
|
-
const
|
|
145
|
-
if (typeof
|
|
163
|
+
const depSrcPaths = this.customData.pathsByLocator.get(targetDependency.locatorHash);
|
|
164
|
+
if (typeof depSrcPaths === `undefined`)
|
|
146
165
|
throw new Error(`Assertion failed: Expected the package to have been registered (${core_1.structUtils.stringifyLocator(dependency)})`);
|
|
147
166
|
const name = core_1.structUtils.stringifyIdent(descriptor);
|
|
148
|
-
const depDstPath = fslib_1.ppath.join(
|
|
149
|
-
const depLinkPath = fslib_1.ppath.relative(fslib_1.ppath.dirname(depDstPath),
|
|
167
|
+
const depDstPath = fslib_1.ppath.join(dependenciesLocation, name);
|
|
168
|
+
const depLinkPath = fslib_1.ppath.relative(fslib_1.ppath.dirname(depDstPath), depSrcPaths.packageLocation);
|
|
150
169
|
const existing = extraneous.get(name);
|
|
151
170
|
extraneous.delete(name);
|
|
152
171
|
concurrentPromises.push(Promise.resolve().then(async () => {
|
|
@@ -160,15 +179,23 @@ class PnpmInstaller {
|
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
await fslib_1.xfs.mkdirpPromise(fslib_1.ppath.dirname(depDstPath));
|
|
163
|
-
if (process.platform == `win32`) {
|
|
164
|
-
await fslib_1.xfs.symlinkPromise(
|
|
182
|
+
if (process.platform == `win32` && this.opts.project.configuration.get(`winLinkType`) === core_1.WindowsLinkType.JUNCTIONS) {
|
|
183
|
+
await fslib_1.xfs.symlinkPromise(depSrcPaths.packageLocation, depDstPath, `junction`);
|
|
165
184
|
}
|
|
166
185
|
else {
|
|
167
186
|
await fslib_1.xfs.symlinkPromise(depLinkPath, depDstPath);
|
|
168
187
|
}
|
|
169
188
|
}));
|
|
189
|
+
};
|
|
190
|
+
let hasExplicitSelfDependency = false;
|
|
191
|
+
for (const [descriptor, dependency] of dependencies) {
|
|
192
|
+
if (descriptor.identHash === locator.identHash)
|
|
193
|
+
hasExplicitSelfDependency = true;
|
|
194
|
+
installDependency(descriptor, dependency);
|
|
170
195
|
}
|
|
171
|
-
|
|
196
|
+
if (!hasExplicitSelfDependency && !this.opts.project.tryWorkspaceByLocator(locator))
|
|
197
|
+
installDependency(core_1.structUtils.convertLocatorToDescriptor(locator), locator);
|
|
198
|
+
concurrentPromises.push(cleanNodeModules(dependenciesLocation, extraneous));
|
|
172
199
|
await Promise.all(concurrentPromises);
|
|
173
200
|
});
|
|
174
201
|
}
|
|
@@ -181,46 +208,25 @@ class PnpmInstaller {
|
|
|
181
208
|
await fslib_1.xfs.removePromise(storeLocation);
|
|
182
209
|
}
|
|
183
210
|
else {
|
|
184
|
-
|
|
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;
|
|
211
|
+
let extraneous;
|
|
214
212
|
try {
|
|
215
|
-
|
|
213
|
+
extraneous = new Set(await fslib_1.xfs.readdirPromise(storeLocation));
|
|
216
214
|
}
|
|
217
215
|
catch {
|
|
218
|
-
|
|
216
|
+
extraneous = new Set();
|
|
217
|
+
}
|
|
218
|
+
for (const { dependenciesLocation } of this.customData.pathsByLocator.values()) {
|
|
219
|
+
if (!dependenciesLocation)
|
|
220
|
+
continue;
|
|
221
|
+
const subpath = fslib_1.ppath.contains(storeLocation, dependenciesLocation);
|
|
222
|
+
if (subpath === null)
|
|
223
|
+
continue;
|
|
224
|
+
const [storeEntry] = subpath.split(fslib_1.ppath.sep);
|
|
225
|
+
extraneous.delete(storeEntry);
|
|
219
226
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
await Promise.all(removals);
|
|
227
|
+
await Promise.all([...extraneous].map(async (extraneousEntry) => {
|
|
228
|
+
await fslib_1.xfs.removePromise(fslib_1.ppath.join(storeLocation, extraneousEntry));
|
|
229
|
+
}));
|
|
224
230
|
}
|
|
225
231
|
// Wait for the package installs to catch up
|
|
226
232
|
await this.asyncActions.wait();
|
|
@@ -232,23 +238,18 @@ class PnpmInstaller {
|
|
|
232
238
|
};
|
|
233
239
|
}
|
|
234
240
|
}
|
|
235
|
-
function getCustomDataKey() {
|
|
236
|
-
return JSON.stringify({
|
|
237
|
-
name: `PnpmInstaller`,
|
|
238
|
-
version: 2,
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
241
|
function getNodeModulesLocation(project) {
|
|
242
242
|
return fslib_1.ppath.join(project.cwd, fslib_1.Filename.nodeModules);
|
|
243
243
|
}
|
|
244
244
|
function getStoreLocation(project) {
|
|
245
245
|
return fslib_1.ppath.join(getNodeModulesLocation(project), `.store`);
|
|
246
246
|
}
|
|
247
|
-
function
|
|
247
|
+
function getPackagePaths(locator, { project }) {
|
|
248
248
|
const pkgKey = core_1.structUtils.slugifyLocator(locator);
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
249
|
+
const storeLocation = getStoreLocation(project);
|
|
250
|
+
const packageLocation = fslib_1.ppath.join(storeLocation, pkgKey, `package`);
|
|
251
|
+
const dependenciesLocation = fslib_1.ppath.join(storeLocation, pkgKey, fslib_1.Filename.nodeModules);
|
|
252
|
+
return { packageLocation, dependenciesLocation };
|
|
252
253
|
}
|
|
253
254
|
function isPnpmVirtualCompatible(locator, { project }) {
|
|
254
255
|
// The pnpm install strategy has a limitation: because Node would always
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PnpmLinker = void 0;
|
|
3
4
|
const PnpmLinker_1 = require("./PnpmLinker");
|
|
5
|
+
Object.defineProperty(exports, "PnpmLinker", { enumerable: true, get: function () { return PnpmLinker_1.PnpmLinker; } });
|
|
4
6
|
const plugin = {
|
|
5
7
|
linkers: [
|
|
6
8
|
PnpmLinker_1.PnpmLinker,
|
package/package.json
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yarnpkg/plugin-pnpm",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.30",
|
|
4
4
|
"license": "BSD-2-Clause",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./lib/index.js",
|
|
8
|
+
"./package.json": "./package.json"
|
|
9
|
+
},
|
|
6
10
|
"dependencies": {
|
|
7
|
-
"@yarnpkg/fslib": "^3.0.0-rc.
|
|
8
|
-
"@yarnpkg/plugin-pnp": "^4.0.0-rc.
|
|
9
|
-
"@yarnpkg/plugin-stage": "^4.0.0-rc.
|
|
11
|
+
"@yarnpkg/fslib": "^3.0.0-rc.30",
|
|
12
|
+
"@yarnpkg/plugin-pnp": "^4.0.0-rc.30",
|
|
13
|
+
"@yarnpkg/plugin-stage": "^4.0.0-rc.30",
|
|
10
14
|
"clipanion": "^3.2.0-rc.10",
|
|
11
15
|
"p-limit": "^2.2.0",
|
|
12
|
-
"tslib": "^
|
|
16
|
+
"tslib": "^2.4.0"
|
|
13
17
|
},
|
|
14
18
|
"peerDependencies": {
|
|
15
|
-
"@yarnpkg/cli": "^4.0.0-rc.
|
|
16
|
-
"@yarnpkg/core": "^4.0.0-rc.
|
|
19
|
+
"@yarnpkg/cli": "^4.0.0-rc.30",
|
|
20
|
+
"@yarnpkg/core": "^4.0.0-rc.30"
|
|
17
21
|
},
|
|
18
22
|
"devDependencies": {
|
|
19
|
-
"@yarnpkg/cli": "^4.0.0-rc.
|
|
20
|
-
"@yarnpkg/core": "^4.0.0-rc.
|
|
23
|
+
"@yarnpkg/cli": "^4.0.0-rc.30",
|
|
24
|
+
"@yarnpkg/core": "^4.0.0-rc.30"
|
|
21
25
|
},
|
|
22
26
|
"repository": {
|
|
23
27
|
"type": "git",
|
|
@@ -30,7 +34,10 @@
|
|
|
30
34
|
},
|
|
31
35
|
"publishConfig": {
|
|
32
36
|
"main": "./lib/index.js",
|
|
33
|
-
"
|
|
37
|
+
"exports": {
|
|
38
|
+
".": "./lib/index.js",
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
}
|
|
34
41
|
},
|
|
35
42
|
"files": [
|
|
36
43
|
"/lib/**/*"
|
|
@@ -38,6 +45,5 @@
|
|
|
38
45
|
"engines": {
|
|
39
46
|
"node": ">=14.15.0"
|
|
40
47
|
},
|
|
41
|
-
"stableVersion": "1.1.0"
|
|
42
|
-
"typings": "./lib/index.d.ts"
|
|
48
|
+
"stableVersion": "1.1.0"
|
|
43
49
|
}
|