@pnpm/installing.deps-resolver 1008.3.1

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.
Files changed (47) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +17 -0
  3. package/lib/dedupeInjectedDeps.d.ts +14 -0
  4. package/lib/dedupeInjectedDeps.js +72 -0
  5. package/lib/depPathToRef.d.ts +4 -0
  6. package/lib/depPathToRef.js +7 -0
  7. package/lib/getCatalogSnapshots.d.ts +4 -0
  8. package/lib/getCatalogSnapshots.js +21 -0
  9. package/lib/getExactSinglePreferredVersions.d.ts +6 -0
  10. package/lib/getExactSinglePreferredVersions.js +11 -0
  11. package/lib/getNonDevWantedDependencies.d.ts +12 -0
  12. package/lib/getNonDevWantedDependencies.js +29 -0
  13. package/lib/getWantedDependencies.d.ts +14 -0
  14. package/lib/getWantedDependencies.js +45 -0
  15. package/lib/hoistPeers.d.ts +10 -0
  16. package/lib/hoistPeers.js +75 -0
  17. package/lib/index.d.ts +53 -0
  18. package/lib/index.js +335 -0
  19. package/lib/mergePeers.d.ts +7 -0
  20. package/lib/mergePeers.js +30 -0
  21. package/lib/nextNodeId.d.ts +6 -0
  22. package/lib/nextNodeId.js +5 -0
  23. package/lib/parentIdsContainSequence.d.ts +2 -0
  24. package/lib/parentIdsContainSequence.js +9 -0
  25. package/lib/replaceVersionInBareSpecifier.d.ts +1 -0
  26. package/lib/replaceVersionInBareSpecifier.js +15 -0
  27. package/lib/resolveDependencies.d.ts +248 -0
  28. package/lib/resolveDependencies.js +1182 -0
  29. package/lib/resolveDependencyTree.d.ts +122 -0
  30. package/lib/resolveDependencyTree.js +228 -0
  31. package/lib/resolvePeers.d.ts +59 -0
  32. package/lib/resolvePeers.js +702 -0
  33. package/lib/safeIsInnerLink.d.ts +6 -0
  34. package/lib/safeIsInnerLink.js +31 -0
  35. package/lib/toResolveImporter.d.ts +22 -0
  36. package/lib/toResolveImporter.js +117 -0
  37. package/lib/unwrapPackageName.d.ts +23 -0
  38. package/lib/unwrapPackageName.js +39 -0
  39. package/lib/updateLockfile.d.ts +10 -0
  40. package/lib/updateLockfile.js +121 -0
  41. package/lib/updateProjectManifest.d.ts +8 -0
  42. package/lib/updateProjectManifest.js +34 -0
  43. package/lib/validatePeerDependencies.d.ts +6 -0
  44. package/lib/validatePeerDependencies.js +15 -0
  45. package/lib/wantedDepIsLocallyAvailable.d.ts +6 -0
  46. package/lib/wantedDepIsLocallyAvailable.js +23 -0
  47. package/package.json +91 -0
@@ -0,0 +1,122 @@
1
+ import type { Catalogs } from '@pnpm/catalogs.types';
2
+ import type { LockfileObject } from '@pnpm/lockfile.types';
3
+ import type { PatchGroupRecord } from '@pnpm/patching.config';
4
+ import type { PreferredVersions, Resolution, WorkspacePackages } from '@pnpm/resolving.resolver-base';
5
+ import type { StoreController } from '@pnpm/store.controller-types';
6
+ import type { AllowBuild, AllowedDeprecatedVersions, PinnedVersion, PkgResolutionId, ProjectId, ProjectManifest, ProjectRootDir, ReadPackageHook, Registries, SupportedArchitectures, TrustPolicy } from '@pnpm/types';
7
+ import type { WantedDependency } from './getNonDevWantedDependencies.js';
8
+ import { type NodeId } from './nextNodeId.js';
9
+ import { type DependenciesTree, type LinkedDependency, type ResolvedPackage, type ResolvedPkgsById } from './resolveDependencies.js';
10
+ export type { DependenciesTree, DependenciesTreeNode, LinkedDependency, ResolvedPackage } from './resolveDependencies.js';
11
+ export interface ResolvedImporters {
12
+ [id: string]: {
13
+ directDependencies: ResolvedDirectDependency[];
14
+ directNodeIdsByAlias: Map<string, NodeId>;
15
+ linkedDependencies: LinkedDependency[];
16
+ };
17
+ }
18
+ export interface ResolvedDirectDependency {
19
+ alias: string;
20
+ optional: boolean;
21
+ dev: boolean;
22
+ resolution: Resolution;
23
+ pkgId: PkgResolutionId;
24
+ version: string;
25
+ name: string;
26
+ catalogLookup?: CatalogLookupMetadata;
27
+ normalizedBareSpecifier?: string;
28
+ }
29
+ /**
30
+ * Information related to the catalog entry for this dependency if it was
31
+ * requested through the catalog protocol.
32
+ */
33
+ export interface CatalogLookupMetadata {
34
+ readonly catalogName: string;
35
+ readonly specifier: string;
36
+ /**
37
+ * The catalog protocol bareSpecifier the user wrote in package.json files or as a
38
+ * parameter to pnpm add. Ex: pnpm add foo@catalog:
39
+ *
40
+ * This will usually be 'catalog:<name>', but can simply be 'catalog:' if
41
+ * users wrote the default catalog shorthand. This is different than the
42
+ * catalogName field, which would be 'default' regardless of whether users
43
+ * originally requested 'catalog:' or 'catalog:default'.
44
+ */
45
+ readonly userSpecifiedBareSpecifier: string;
46
+ }
47
+ export interface Importer<WantedDepExtraProps> {
48
+ id: ProjectId;
49
+ manifest: ProjectManifest;
50
+ modulesDir: string;
51
+ removePackages?: string[];
52
+ rootDir: ProjectRootDir;
53
+ wantedDependencies: Array<WantedDepExtraProps & WantedDependency>;
54
+ }
55
+ export interface ImporterToResolveGeneric<WantedDepExtraProps> extends Importer<WantedDepExtraProps> {
56
+ updatePackageManifest: boolean;
57
+ updateMatching?: (pkgName: string, version?: string) => boolean;
58
+ updateToLatest?: boolean;
59
+ hasRemovedDependencies?: boolean;
60
+ preferredVersions?: PreferredVersions;
61
+ wantedDependencies: Array<WantedDepExtraProps & WantedDependency & {
62
+ updateDepth: number;
63
+ }>;
64
+ pinnedVersion?: PinnedVersion;
65
+ }
66
+ export interface ResolveDependenciesOptions {
67
+ allowBuild?: AllowBuild;
68
+ autoInstallPeers?: boolean;
69
+ autoInstallPeersFromHighestMatch?: boolean;
70
+ allowedDeprecatedVersions: AllowedDeprecatedVersions;
71
+ allowUnusedPatches: boolean;
72
+ catalogs?: Catalogs;
73
+ currentLockfile: LockfileObject;
74
+ dedupePeerDependents?: boolean;
75
+ dryRun: boolean;
76
+ engineStrict: boolean;
77
+ force: boolean;
78
+ forceFullResolution: boolean;
79
+ ignoreScripts?: boolean;
80
+ hooks: {
81
+ readPackage?: ReadPackageHook;
82
+ };
83
+ nodeVersion?: string;
84
+ registries: Registries;
85
+ patchedDependencies?: PatchGroupRecord;
86
+ pnpmVersion: string;
87
+ preferredVersions?: PreferredVersions;
88
+ preferWorkspacePackages?: boolean;
89
+ resolutionMode?: 'highest' | 'time-based' | 'lowest-direct';
90
+ resolvePeersFromWorkspaceRoot?: boolean;
91
+ injectWorkspacePackages?: boolean;
92
+ linkWorkspacePackagesDepth?: number;
93
+ lockfileDir: string;
94
+ storeController: StoreController;
95
+ tag: string;
96
+ virtualStoreDir: string;
97
+ globalVirtualStoreDir: string;
98
+ virtualStoreDirMaxLength: number;
99
+ wantedLockfile: LockfileObject;
100
+ workspacePackages: WorkspacePackages;
101
+ supportedArchitectures?: SupportedArchitectures;
102
+ peersSuffixMaxLength: number;
103
+ minimumReleaseAge?: number;
104
+ minimumReleaseAgeExclude?: string[];
105
+ trustPolicy?: TrustPolicy;
106
+ trustPolicyExclude?: string[];
107
+ trustPolicyIgnoreAfter?: number;
108
+ blockExoticSubdeps?: boolean;
109
+ }
110
+ export interface ResolveDependencyTreeResult {
111
+ allPeerDepNames: Set<string>;
112
+ dependenciesTree: DependenciesTree<ResolvedPackage>;
113
+ outdatedDependencies: {
114
+ [pkgId: string]: string;
115
+ };
116
+ resolvedImporters: ResolvedImporters;
117
+ resolvedPkgsById: ResolvedPkgsById;
118
+ wantedToBeSkippedPackageIds: Set<string>;
119
+ appliedPatches: Set<string>;
120
+ time?: Record<string, string>;
121
+ }
122
+ export declare function resolveDependencyTree<T>(importers: Array<ImporterToResolveGeneric<T>>, opts: ResolveDependenciesOptions): Promise<ResolveDependencyTreeResult>;
@@ -0,0 +1,228 @@
1
+ import { resolveFromCatalog } from '@pnpm/catalogs.resolver';
2
+ import { createPackageVersionPolicy } from '@pnpm/config.version-policy';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import { globalWarn } from '@pnpm/logger';
5
+ import { partition, zipObj } from 'ramda';
6
+ import { nextNodeId } from './nextNodeId.js';
7
+ import { parentIdsContainSequence } from './parentIdsContainSequence.js';
8
+ import { resolveRootDependencies, } from './resolveDependencies.js';
9
+ export async function resolveDependencyTree(importers, opts) {
10
+ const wantedToBeSkippedPackageIds = new Set();
11
+ const autoInstallPeers = opts.autoInstallPeers === true;
12
+ const ctx = {
13
+ allowBuild: opts.allowBuild,
14
+ autoInstallPeers,
15
+ autoInstallPeersFromHighestMatch: opts.autoInstallPeersFromHighestMatch === true,
16
+ allowedDeprecatedVersions: opts.allowedDeprecatedVersions,
17
+ catalogResolver: resolveFromCatalog.bind(null, opts.catalogs ?? {}),
18
+ childrenByParentId: {},
19
+ currentLockfile: opts.currentLockfile,
20
+ defaultTag: opts.tag,
21
+ dependenciesTree: new Map(),
22
+ dryRun: opts.dryRun,
23
+ engineStrict: opts.engineStrict,
24
+ force: opts.force,
25
+ forceFullResolution: opts.forceFullResolution,
26
+ ignoreScripts: opts.ignoreScripts,
27
+ injectWorkspacePackages: opts.injectWorkspacePackages,
28
+ linkWorkspacePackagesDepth: opts.linkWorkspacePackagesDepth ?? -1,
29
+ lockfileDir: opts.lockfileDir,
30
+ nodeVersion: opts.nodeVersion,
31
+ outdatedDependencies: {},
32
+ patchedDependencies: opts.patchedDependencies,
33
+ pendingNodes: [],
34
+ pnpmVersion: opts.pnpmVersion,
35
+ preferWorkspacePackages: opts.preferWorkspacePackages,
36
+ readPackageHook: opts.hooks.readPackage,
37
+ registries: opts.registries,
38
+ resolvedPkgsById: {},
39
+ resolvePeersFromWorkspaceRoot: opts.resolvePeersFromWorkspaceRoot,
40
+ resolutionMode: opts.resolutionMode,
41
+ skipped: wantedToBeSkippedPackageIds,
42
+ storeController: opts.storeController,
43
+ virtualStoreDir: opts.virtualStoreDir,
44
+ virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
45
+ wantedLockfile: opts.wantedLockfile,
46
+ appliedPatches: new Set(),
47
+ updatedSet: new Set(),
48
+ workspacePackages: opts.workspacePackages,
49
+ missingPeersOfChildrenByPkgId: {},
50
+ hoistPeers: autoInstallPeers || opts.dedupePeerDependents,
51
+ allPeerDepNames: new Set(),
52
+ maximumPublishedBy: opts.minimumReleaseAge ? new Date(Date.now() - opts.minimumReleaseAge * 60 * 1000) : undefined,
53
+ publishedByExclude: opts.minimumReleaseAgeExclude ? createPackageVersionPolicyByExclude(opts.minimumReleaseAgeExclude, 'minimumReleaseAgeExclude') : undefined,
54
+ trustPolicy: opts.trustPolicy,
55
+ trustPolicyExclude: opts.trustPolicyExclude ? createPackageVersionPolicyByExclude(opts.trustPolicyExclude, 'trustPolicyExclude') : undefined,
56
+ trustPolicyIgnoreAfter: opts.trustPolicyIgnoreAfter,
57
+ blockExoticSubdeps: opts.blockExoticSubdeps,
58
+ };
59
+ function createPackageVersionPolicyByExclude(patterns, key) {
60
+ try {
61
+ return createPackageVersionPolicy(patterns);
62
+ }
63
+ catch (err) {
64
+ if (!err || typeof err !== 'object' || !('message' in err))
65
+ throw err;
66
+ throw new PnpmError(`INVALID_${key.replace(/([A-Z])/g, '_$1').toUpperCase()}`, `Invalid value in ${key}: ${err.message}`);
67
+ }
68
+ }
69
+ const resolveArgs = importers.map((importer) => {
70
+ const projectSnapshot = opts.wantedLockfile.importers[importer.id];
71
+ // This may be optimized.
72
+ // We only need to proceed resolving every dependency
73
+ // if the newly added dependency has peer dependencies.
74
+ const proceed = importer.id === '.' || importer.hasRemovedDependencies === true || importer.wantedDependencies.some((wantedDep) => wantedDep.isNew); // eslint-disable-line @typescript-eslint/no-explicit-any
75
+ const resolveOpts = {
76
+ currentDepth: 0,
77
+ parentPkg: {
78
+ installable: true,
79
+ nodeId: importer.id,
80
+ optional: false,
81
+ pkgId: importer.id,
82
+ rootDir: importer.rootDir,
83
+ },
84
+ parentIds: [importer.id],
85
+ proceed,
86
+ resolvedDependencies: {
87
+ ...projectSnapshot.dependencies,
88
+ ...projectSnapshot.devDependencies,
89
+ ...projectSnapshot.optionalDependencies,
90
+ },
91
+ updateDepth: -1,
92
+ updateMatching: importer.updateMatching,
93
+ updateToLatest: importer.updateToLatest,
94
+ prefix: importer.rootDir,
95
+ supportedArchitectures: opts.supportedArchitectures,
96
+ };
97
+ return {
98
+ updatePackageManifest: importer.updatePackageManifest,
99
+ parentPkgAliases: Object.fromEntries(importer.wantedDependencies.filter(({ alias }) => alias).map(({ alias }) => [alias, true])),
100
+ preferredVersions: importer.preferredVersions ?? {},
101
+ wantedDependencies: importer.wantedDependencies,
102
+ options: resolveOpts,
103
+ pinnedVersion: importer.pinnedVersion,
104
+ };
105
+ });
106
+ const { pkgAddressesByImporters, time } = await resolveRootDependencies(ctx, resolveArgs);
107
+ const directDepsByImporterId = zipObj(importers.map(({ id }) => id), pkgAddressesByImporters);
108
+ for (const directDependencies of pkgAddressesByImporters) {
109
+ for (const directDep of directDependencies) {
110
+ const { alias, normalizedBareSpecifier, version, saveCatalogName } = directDep;
111
+ if (saveCatalogName == null) {
112
+ continue;
113
+ }
114
+ const existingCatalog = opts.catalogs?.default?.[alias];
115
+ if (existingCatalog != null) {
116
+ if (existingCatalog !== normalizedBareSpecifier) {
117
+ globalWarn(`Skip adding ${alias} to the default catalog because it already exists as ${existingCatalog}. Please use \`pnpm update\` to update the catalogs.`);
118
+ }
119
+ }
120
+ else if (normalizedBareSpecifier != null && version != null) {
121
+ const userSpecifiedBareSpecifier = `catalog:${saveCatalogName === 'default' ? '' : saveCatalogName}`;
122
+ // Attach metadata about how this new catalog dependency should be
123
+ // resolved so the pnpm-lock.yaml file's catalogs section can be updated
124
+ // to reflect this newly added entry.
125
+ directDep.catalogLookup = {
126
+ catalogName: saveCatalogName,
127
+ specifier: normalizedBareSpecifier,
128
+ userSpecifiedBareSpecifier,
129
+ };
130
+ }
131
+ }
132
+ }
133
+ for (const pendingNode of ctx.pendingNodes) {
134
+ ctx.dependenciesTree.set(pendingNode.nodeId, {
135
+ children: () => buildTree(ctx, pendingNode.resolvedPackage.id, pendingNode.parentIds, ctx.childrenByParentId[pendingNode.resolvedPackage.id], pendingNode.depth + 1, pendingNode.installable),
136
+ depth: pendingNode.depth,
137
+ installable: pendingNode.installable,
138
+ resolvedPackage: pendingNode.resolvedPackage,
139
+ });
140
+ }
141
+ const resolvedImporters = {};
142
+ for (const { id, wantedDependencies } of importers) {
143
+ const directDeps = dedupeSameAliasDirectDeps(directDepsByImporterId[id], wantedDependencies);
144
+ const [linkedDependencies, directNonLinkedDeps] = partition((dep) => dep.isLinkedDependency === true, directDeps);
145
+ resolvedImporters[id] = {
146
+ directDependencies: directDeps
147
+ .map((dep) => {
148
+ if (dep.isLinkedDependency === true) {
149
+ return dep;
150
+ }
151
+ const resolvedPackage = ctx.dependenciesTree.get(dep.nodeId).resolvedPackage;
152
+ return {
153
+ alias: dep.alias,
154
+ catalogLookup: dep.catalogLookup,
155
+ dev: resolvedPackage.dev,
156
+ name: resolvedPackage.name,
157
+ optional: resolvedPackage.optional,
158
+ pkgId: resolvedPackage.id,
159
+ resolution: resolvedPackage.resolution,
160
+ version: resolvedPackage.version,
161
+ normalizedBareSpecifier: dep.normalizedBareSpecifier,
162
+ };
163
+ }),
164
+ directNodeIdsByAlias: new Map(directNonLinkedDeps.map(({ alias, nodeId }) => [alias, nodeId])),
165
+ linkedDependencies,
166
+ };
167
+ }
168
+ return {
169
+ dependenciesTree: ctx.dependenciesTree,
170
+ outdatedDependencies: ctx.outdatedDependencies,
171
+ resolvedImporters,
172
+ resolvedPkgsById: ctx.resolvedPkgsById,
173
+ wantedToBeSkippedPackageIds,
174
+ appliedPatches: ctx.appliedPatches,
175
+ time,
176
+ allPeerDepNames: ctx.allPeerDepNames,
177
+ };
178
+ }
179
+ function buildTree(ctx, parentId, parentIds, children, depth, installable) {
180
+ const childrenNodeIds = {};
181
+ for (const child of children) {
182
+ if (child.id.startsWith('link:')) {
183
+ childrenNodeIds[child.alias] = child.id;
184
+ continue;
185
+ }
186
+ if (parentIdsContainSequence(parentIds, parentId, child.id) || parentId === child.id) {
187
+ continue;
188
+ }
189
+ if (ctx.resolvedPkgsById[child.id].isLeaf) {
190
+ childrenNodeIds[child.alias] = child.id;
191
+ continue;
192
+ }
193
+ const childNodeId = nextNodeId();
194
+ childrenNodeIds[child.alias] = childNodeId;
195
+ installable = installable || !ctx.skipped.has(child.id);
196
+ ctx.dependenciesTree.set(childNodeId, {
197
+ children: () => buildTree(ctx, child.id, [...parentIds, child.id], ctx.childrenByParentId[child.id], depth + 1, installable),
198
+ depth,
199
+ installable,
200
+ resolvedPackage: ctx.resolvedPkgsById[child.id],
201
+ });
202
+ }
203
+ return childrenNodeIds;
204
+ }
205
+ /**
206
+ * There may be cases where multiple dependencies have the same alias in the directDeps array.
207
+ * E.g., when there is "is-negative: github:kevva/is-negative#1.0.0" in the package.json dependencies,
208
+ * and then re-execute `pnpm add github:kevva/is-negative#1.0.1`.
209
+ * In order to make sure that the latest 1.0.1 version is installed, we need to remove the duplicate dependency.
210
+ * fix https://github.com/pnpm/pnpm/issues/6966
211
+ */
212
+ function dedupeSameAliasDirectDeps(directDeps, wantedDependencies) {
213
+ const deps = new Map();
214
+ for (const directDep of directDeps) {
215
+ const { alias, normalizedBareSpecifier } = directDep;
216
+ if (!deps.has(alias)) {
217
+ deps.set(alias, directDep);
218
+ }
219
+ else {
220
+ const wantedDep = wantedDependencies.find(dep => dep.alias ? dep.alias === alias : dep.bareSpecifier === normalizedBareSpecifier);
221
+ if (wantedDep?.isNew) {
222
+ deps.set(alias, directDep);
223
+ }
224
+ }
225
+ }
226
+ return Array.from(deps.values());
227
+ }
228
+ //# sourceMappingURL=resolveDependencyTree.js.map
@@ -0,0 +1,59 @@
1
+ import type { DepPath, PeerDependencyIssuesByProjects, ProjectRootDir } from '@pnpm/types';
2
+ import type { NodeId } from './nextNodeId.js';
3
+ import type { DependenciesTree, PeerDependencies, ResolvedPackage } from './resolveDependencies.js';
4
+ import type { ResolvedImporters } from './resolveDependencyTree.js';
5
+ export interface BaseGenericDependenciesGraphNode {
6
+ modules: string;
7
+ dir: string;
8
+ depPath: DepPath;
9
+ depth: number;
10
+ peerDependencies?: PeerDependencies;
11
+ transitivePeerDependencies: Set<string>;
12
+ installable: boolean;
13
+ isBuilt?: boolean;
14
+ isPure: boolean;
15
+ resolvedPeerNames: Set<string>;
16
+ requiresBuild?: boolean;
17
+ }
18
+ export interface GenericDependenciesGraphNode extends BaseGenericDependenciesGraphNode {
19
+ childrenNodeIds: Record<string, NodeId>;
20
+ }
21
+ export interface GenericDependenciesGraphNodeWithResolvedChildren extends BaseGenericDependenciesGraphNode {
22
+ children: Record<string, DepPath>;
23
+ }
24
+ export type PartialResolvedPackage = Pick<ResolvedPackage, 'id' | 'pkgIdWithPatchHash' | 'name' | 'peerDependencies' | 'version'>;
25
+ export interface GenericDependenciesGraph<T extends PartialResolvedPackage> {
26
+ [depPath: DepPath]: T & GenericDependenciesGraphNode;
27
+ }
28
+ export interface GenericDependenciesGraphWithResolvedChildren<T extends PartialResolvedPackage> {
29
+ [depPath: DepPath]: T & GenericDependenciesGraphNodeWithResolvedChildren;
30
+ }
31
+ export interface ProjectToResolve {
32
+ directNodeIdsByAlias: Map<string, NodeId>;
33
+ topParents: Array<{
34
+ name: string;
35
+ version: string;
36
+ alias?: string;
37
+ }>;
38
+ rootDir: ProjectRootDir;
39
+ id: string;
40
+ }
41
+ export type DependenciesByProjectId = Record<string, Map<string, DepPath>>;
42
+ export declare function resolvePeers<T extends PartialResolvedPackage>(opts: {
43
+ allPeerDepNames: Set<string>;
44
+ projects: ProjectToResolve[];
45
+ dependenciesTree: DependenciesTree<T>;
46
+ virtualStoreDir: string;
47
+ virtualStoreDirMaxLength: number;
48
+ lockfileDir: string;
49
+ resolvePeersFromWorkspaceRoot?: boolean;
50
+ dedupePeerDependents?: boolean;
51
+ dedupeInjectedDeps?: boolean;
52
+ resolvedImporters: ResolvedImporters;
53
+ peersSuffixMaxLength: number;
54
+ workspaceProjectIds: Set<string>;
55
+ }): Promise<{
56
+ dependenciesGraph: GenericDependenciesGraphWithResolvedChildren<T>;
57
+ dependenciesByProjectId: DependenciesByProjectId;
58
+ peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects;
59
+ }>;