0
# Version Selection
1
2
The version selection system handles intelligent package version resolution based on preferences, constraints, and semantic versioning rules with support for tags, ranges, and exact versions.
3
4
## Capabilities
5
6
### Package Specification Types
7
8
Core types used for version resolution within the resolver.
9
10
```typescript { .api }
11
/**
12
* Structured package specification for version resolution
13
*/
14
interface RegistryPackageSpec {
15
/** Type of specification */
16
type: 'tag' | 'version' | 'range';
17
/** Package name */
18
name: string;
19
/** Specification string to match against */
20
fetchSpec: string;
21
/** Normalized preference for caching */
22
normalizedPref?: string;
23
}
24
```
25
26
### Version Preference Configuration
27
28
Configure preferred versions for specific packages during resolution.
29
30
```typescript { .api }
31
/**
32
* Version preference configuration for specific packages
33
*/
34
interface PreferredVersions {
35
[packageName: string]: {
36
/** Version selector (exact version, range, or tag) */
37
selector: string;
38
/** Type of version selector */
39
type: 'version' | 'range' | 'tag';
40
};
41
}
42
43
/**
44
* Options for package resolution
45
*/
46
interface ResolveOptions {
47
/** Default tag when no preference specified */
48
defaultTag?: string;
49
/** Registry URL to resolve from */
50
registry: string;
51
/** Preferred versions for specific packages */
52
preferredVersions?: PreferredVersions;
53
/** Project root prefix */
54
prefix: string;
55
/** Available local packages for fallback */
56
localPackages?: LocalPackages;
57
}
58
```
59
60
### Package Selection Logic
61
62
The resolver internally handles package version selection from metadata based on the specification type and preferred versions configuration. This process is handled automatically when calling the resolver function.
63
64
## Usage Examples
65
66
### Basic Version Selection
67
68
```typescript
69
import createResolveFromNpm from '@pnpm/npm-resolver';
70
71
const resolve = createResolveFromNpm({
72
metaCache: new Map(),
73
store: './pnpm-store',
74
rawNpmConfig: { registry: 'https://registry.npmjs.org/' },
75
});
76
77
// Exact version
78
const exactResult = await resolve(
79
{ alias: 'lodash', pref: '4.17.21' },
80
{
81
registry: 'https://registry.npmjs.org/',
82
prefix: process.cwd()
83
}
84
);
85
86
// Semantic version range
87
const rangeResult = await resolve(
88
{ alias: 'express', pref: '^4.18.0' },
89
{
90
registry: 'https://registry.npmjs.org/',
91
prefix: process.cwd()
92
}
93
);
94
95
// Tag-based selection
96
const tagResult = await resolve(
97
{ alias: 'react', pref: 'beta' },
98
{
99
registry: 'https://registry.npmjs.org/',
100
prefix: process.cwd()
101
}
102
);
103
104
console.log(`Exact: ${exactResult?.package.version}`);
105
console.log(`Range: ${rangeResult?.package.version}`);
106
console.log(`Tag: ${tagResult?.package.version}`);
107
```
108
109
### Advanced Range Selection
110
111
```typescript
112
// Complex version ranges
113
const complexRanges = [
114
{ alias: 'typescript', pref: '~4.9.0' }, // Patch-level changes
115
{ alias: 'webpack', pref: '^5.0.0' }, // Compatible changes
116
{ alias: 'eslint', pref: '>=8.0.0 <9.0.0' }, // Range with constraints
117
{ alias: 'prettier', pref: '2.x' }, // Major version wildcard
118
];
119
120
for (const dep of complexRanges) {
121
const result = await resolve(dep, {
122
registry: 'https://registry.npmjs.org/',
123
prefix: process.cwd()
124
});
125
126
if (result) {
127
console.log(`${dep.alias}@${dep.pref} -> ${result.package.version}`);
128
}
129
}
130
```
131
132
### Preferred Version Configuration
133
134
```typescript
135
// Configure preferred versions for specific packages
136
const preferredVersions = {
137
'react': {
138
selector: '17.0.2', // Prefer specific stable version
139
type: 'version' as const
140
},
141
'lodash': {
142
selector: '^4.17.0', // Prefer range within 4.17.x
143
type: 'range' as const
144
},
145
'typescript': {
146
selector: 'beta', // Prefer beta releases
147
type: 'tag' as const
148
}
149
};
150
151
const result = await resolve(
152
{ alias: 'react', pref: '^17.0.0' },
153
{
154
registry: 'https://registry.npmjs.org/',
155
prefix: process.cwd(),
156
preferredVersions
157
}
158
);
159
160
// Will prefer 17.0.2 even though range allows newer versions
161
console.log(`Selected version: ${result?.package.version}`);
162
```
163
164
### Custom Tag Handling
165
166
```typescript
167
// Working with distribution tags
168
const tagExamples = [
169
{ alias: 'next', pref: 'canary' }, // Canary releases
170
{ alias: 'vue', pref: 'beta' }, // Beta releases
171
{ alias: 'angular', pref: 'next' }, // Next major version
172
{ alias: 'react', pref: 'experimental' }, // Experimental features
173
];
174
175
for (const dep of tagExamples) {
176
try {
177
const result = await resolve(dep, {
178
registry: 'https://registry.npmjs.org/',
179
prefix: process.cwd()
180
});
181
182
if (result) {
183
console.log(`${dep.alias}@${dep.pref}: ${result.package.version}`);
184
console.log(`Latest: ${result.latest}`);
185
}
186
} catch (error) {
187
console.error(`Tag ${dep.pref} not found for ${dep.alias}`);
188
}
189
}
190
```
191
192
### Fallback Strategy
193
194
```typescript
195
// Try multiple version preferences with fallback
196
async function resolveWithFallback(packageName: string, preferences: string[]) {
197
for (const pref of preferences) {
198
try {
199
const result = await resolve(
200
{ alias: packageName, pref },
201
{
202
registry: 'https://registry.npmjs.org/',
203
prefix: process.cwd()
204
}
205
);
206
207
if (result) {
208
console.log(`Resolved ${packageName}@${pref} -> ${result.package.version}`);
209
return result;
210
}
211
} catch (error) {
212
console.log(`Failed to resolve ${packageName}@${pref}: ${error.message}`);
213
}
214
}
215
216
throw new Error(`Could not resolve ${packageName} with any preference`);
217
}
218
219
// Try beta first, fallback to latest
220
const result = await resolveWithFallback('typescript', ['beta', 'rc', 'latest']);
221
```
222
223
### Version Format Support
224
225
The resolver supports various version specification formats:
226
227
```typescript
228
// Different preference formats supported
229
const supportedFormats = [
230
'1.2.3', // Exact version
231
'^1.2.0', // Caret range - compatible within major version
232
'~1.2.0', // Tilde range - compatible within minor version
233
'>=1.0.0 <2.0.0', // Range expression with constraints
234
'latest', // Distribution tag
235
'beta', // Beta distribution tag
236
'next', // Next distribution tag
237
];
238
239
// Example resolution with different formats
240
for (const pref of supportedFormats) {
241
const result = await resolve(
242
{ alias: 'example-package', pref },
243
{
244
registry: 'https://registry.npmjs.org/',
245
prefix: process.cwd()
246
}
247
);
248
249
if (result) {
250
console.log(`"${pref}" resolved to: ${result.package.version}`);
251
}
252
}
253
```
254
255
### Local Package Integration
256
257
```typescript
258
// Define local packages for development
259
const localPackages = {
260
'my-lib': {
261
'1.0.0': {
262
directory: '/workspace/my-lib',
263
package: { name: 'my-lib', version: '1.0.0' }
264
},
265
'1.1.0-dev': {
266
directory: '/workspace/my-lib',
267
package: { name: 'my-lib', version: '1.1.0-dev' }
268
}
269
}
270
};
271
272
// Prefer local development version over registry
273
const devResult = await resolve(
274
{ alias: 'my-lib', pref: '^1.0.0' },
275
{
276
registry: 'https://registry.npmjs.org/',
277
prefix: process.cwd(),
278
localPackages
279
}
280
);
281
282
if (devResult?.resolvedVia === 'local-filesystem') {
283
console.log(`Using local development version: ${devResult.package.version}`);
284
console.log(`Location: ${devResult.resolution.directory}`);
285
} else {
286
console.log(`Using registry version: ${devResult?.package.version}`);
287
}
288
```
289
290
### Version Preference Validation
291
292
```typescript
293
// Validate version preferences through resolution attempt
294
async function validateVersionPreference(alias: string, pref: string): Promise<boolean> {
295
try {
296
const result = await resolve(
297
{ alias, pref },
298
{
299
registry: 'https://registry.npmjs.org/',
300
prefix: process.cwd()
301
}
302
);
303
304
return result !== null;
305
} catch (error) {
306
// Invalid preference or resolution failed
307
return false;
308
}
309
}
310
311
// Test various preferences
312
const testPreferences = [
313
{ alias: 'lodash', pref: '4.17.21' }, // Valid exact version
314
{ alias: 'lodash', pref: '^4.0.0' }, // Valid range
315
{ alias: 'lodash', pref: 'latest' }, // Valid tag
316
{ alias: 'lodash', pref: 'invalid' }, // Invalid tag
317
];
318
319
for (const { alias, pref } of testPreferences) {
320
const isValid = await validateVersionPreference(alias, pref);
321
console.log(`${alias}@${pref}: ${isValid ? 'valid' : 'invalid'}`);
322
}
323
```