0
# Package Resolution
1
2
Advanced package resolution supporting modern Node.js features like package.json exports/imports fields, conditional exports, and legacy browser field specifications. Handles both standard npm packages and complex multi-entry packages.
3
4
## Capabilities
5
6
### Package Entry Point Resolution
7
8
Resolves the main entry point of a package based on package.json fields.
9
10
```typescript { .api }
11
/**
12
* Resolve the main entry point subpath for a package
13
* @param context - Resolution context
14
* @param packageInfo - Package information including package.json
15
* @param platform - Target platform for conditional resolution
16
* @returns Entry point subpath relative to package root
17
*/
18
function getPackageEntryPoint(
19
context: ResolutionContext,
20
packageInfo: PackageInfo,
21
platform: string | null
22
): string;
23
24
interface PackageInfo {
25
readonly packageJson: PackageJson;
26
readonly rootPath: string;
27
}
28
```
29
30
**Usage Example:**
31
32
```javascript
33
const { getPackageEntryPoint } = require("metro-resolver/src/PackageResolve");
34
35
const packageInfo = {
36
packageJson: {
37
name: "my-package",
38
main: "./dist/index.js",
39
browser: "./dist/browser.js",
40
module: "./dist/esm.js"
41
},
42
rootPath: "/node_modules/my-package"
43
};
44
45
// With mainFields: ['browser', 'module', 'main']
46
const entryPoint = getPackageEntryPoint(context, packageInfo, null);
47
// Result: "./dist/browser.js"
48
```
49
50
### Package.json Structure
51
52
Standard package.json fields supported for resolution.
53
54
```typescript { .api }
55
interface PackageJson {
56
readonly name?: string;
57
readonly main?: string;
58
readonly exports?: string | ExportMap;
59
readonly imports?: ExportMap;
60
readonly browser?: string | BrowserFieldMap;
61
readonly module?: string;
62
readonly types?: string;
63
}
64
65
type BrowserFieldMap = Readonly<{
66
[key: string]: string | false;
67
}>;
68
69
type ExportMap = Readonly<{
70
[subpathOrCondition: string]: ExportMap | string | null;
71
}>;
72
```
73
74
### Package Exports Resolution
75
76
Modern package.json exports field resolution with conditional exports support.
77
78
```typescript { .api }
79
/**
80
* Resolve a package target using the exports field
81
* @param context - Resolution context
82
* @param packagePath - Absolute path to package root
83
* @param absoluteCandidatePath - Absolute path being resolved
84
* @param packageRelativePath - Relative path within package
85
* @param exportsField - Package exports configuration
86
* @param platform - Target platform
87
* @returns Resolved file resolution or null if not found
88
*/
89
function resolvePackageTargetFromExports(
90
context: ResolutionContext,
91
packagePath: string,
92
absoluteCandidatePath: string,
93
packageRelativePath: string,
94
exportsField: ExportsField,
95
platform: string | null
96
): FileResolution | null;
97
98
type ExportsField = string | ExportMap;
99
```
100
101
**Exports Field Examples:**
102
103
```json
104
{
105
"exports": {
106
".": "./dist/index.js",
107
"./utils": "./dist/utils.js",
108
"./package.json": "./package.json"
109
}
110
}
111
```
112
113
```json
114
{
115
"exports": {
116
".": {
117
"import": "./dist/esm/index.js",
118
"require": "./dist/cjs/index.js",
119
"browser": "./dist/browser/index.js"
120
}
121
}
122
}
123
```
124
125
**Platform-Specific Exports:**
126
127
```json
128
{
129
"exports": {
130
".": {
131
"react-native": "./dist/native.js",
132
"browser": "./dist/browser.js",
133
"default": "./dist/node.js"
134
}
135
}
136
}
137
```
138
139
### Package Imports Resolution
140
141
Package.json imports field resolution for subpath imports (starting with `#`).
142
143
```typescript { .api }
144
/**
145
* Resolve a package target using the imports field
146
* @param context - Resolution context
147
* @param packagePath - Absolute path to package root
148
* @param importSpecifier - Import specifier (e.g., "#utils/helper")
149
* @param importsField - Package imports configuration
150
* @param platform - Target platform
151
* @returns Resolved file resolution or null if not found
152
*/
153
function resolvePackageTargetFromImports(
154
context: ResolutionContext,
155
packagePath: string,
156
importSpecifier: string,
157
importsField: ExportMap,
158
platform: string | null
159
): FileResolution | null;
160
```
161
162
**Imports Field Example:**
163
164
```json
165
{
166
"imports": {
167
"#utils/*": "./src/utils/*.js",
168
"#config": {
169
"development": "./config/dev.js",
170
"production": "./config/prod.js"
171
}
172
}
173
}
174
```
175
176
**Usage Example:**
177
178
```javascript
179
// In a file within the package, you can use:
180
import helper from "#utils/helper"; // Resolves to ./src/utils/helper.js
181
import config from "#config"; // Resolves based on condition
182
```
183
184
### Conditional Exports
185
186
Support for conditional exports based on environment and platform.
187
188
```typescript { .api }
189
interface ConditionalExports {
190
[condition: string]: string | ConditionalExports | null;
191
}
192
```
193
194
**Common Conditions:**
195
- `import`: ESM import
196
- `require`: CommonJS require
197
- `browser`: Browser environment
198
- `node`: Node.js environment
199
- `react-native`: React Native platform
200
- `development`/`production`: Build mode
201
202
**Example:**
203
204
```json
205
{
206
"exports": {
207
".": {
208
"import": {
209
"browser": "./dist/esm/browser.js",
210
"node": "./dist/esm/node.js"
211
},
212
"require": {
213
"browser": "./dist/cjs/browser.js",
214
"node": "./dist/cjs/node.js"
215
}
216
}
217
}
218
}
219
```
220
221
### Package Redirection
222
223
Module path redirection through package.json browser field and custom redirections.
224
225
```typescript { .api }
226
/**
227
* Redirect a module path based on package configuration
228
* @param context - Resolution context
229
* @param modulePath - Original module path
230
* @returns Redirected path, original path, or false to exclude
231
*/
232
function redirectModulePath(
233
context: ResolutionContext,
234
modulePath: string
235
): string | false;
236
```
237
238
**Browser Field Redirection:**
239
240
```json
241
{
242
"browser": {
243
"./src/node-specific.js": "./src/browser-specific.js",
244
"fs": false,
245
"path": "path-browserify"
246
}
247
}
248
```
249
250
### Legacy Resolution
251
252
Support for legacy package resolution patterns.
253
254
**Main Field Priority:**
255
1. Check configured `mainFields` in order
256
2. Default to "index" if no main field found
257
3. Attempt file resolution with extensions
258
4. Fallback to index file in directory
259
260
**Browser Field Specification:**
261
- String value: Replace main entry point
262
- Object value: Path-specific redirections
263
- `false` value: Exclude module from bundle
264
265
### Package Resolution Flow
266
267
The complete package resolution process:
268
269
1. **Exports Field Resolution**: If `unstable_enablePackageExports` is true and exports field exists
270
2. **Legacy Resolution**: Fallback to main fields and browser field
271
3. **Entry Point Resolution**: Resolve the determined entry point as a file
272
4. **Index Fallback**: If entry point resolution fails, try index file
273
274
### Error Handling in Package Resolution
275
276
Specific errors thrown during package resolution:
277
278
```typescript { .api }
279
class PackagePathNotExportedError extends Error {
280
packagePath: string;
281
subpath: string;
282
}
283
284
class InvalidPackageConfigurationError extends Error {
285
reason: string;
286
}
287
288
class PackageImportNotResolvedError extends Error {
289
importSpecifier: string;
290
reason: string;
291
}
292
```
293
294
### Subpath Pattern Matching
295
296
Pattern matching for exports and imports with wildcards.
297
298
```typescript { .api }
299
/**
300
* Match a subpath against a pattern with wildcards
301
* @param pattern - Pattern with optional wildcards (*)
302
* @param subpath - Subpath to match against
303
* @returns Match result with captured wildcard values
304
*/
305
function matchSubpathPattern(
306
pattern: string,
307
subpath: string
308
): { matched: boolean; captured: string[] };
309
```
310
311
**Pattern Examples:**
312
313
```json
314
{
315
"exports": {
316
"./lib/*": "./dist/*.js",
317
"./utils/*/helper": "./dist/utils/*/helper.js"
318
}
319
}
320
```
321
322
### Package Detection
323
324
Determine package boundaries and extract package information.
325
326
```typescript { .api }
327
interface PackageForModule extends PackageInfo {
328
/** System-separated subpath relative to package root */
329
readonly packageRelativePath: string;
330
}
331
332
/**
333
* Get package information for a module path
334
* @param absoluteModulePath - Absolute path to check
335
* @returns Package information or null if no package found
336
*/
337
function getPackageForModule(
338
absoluteModulePath: string
339
): PackageForModule | null;
340
```
341
342
**Usage Example:**
343
344
```javascript
345
const packageInfo = context.getPackageForModule('/app/node_modules/lodash/lib/map.js');
346
// Result:
347
// {
348
// packageJson: { name: 'lodash', version: '4.17.21', ... },
349
// rootPath: '/app/node_modules/lodash',
350
// packageRelativePath: 'lib/map.js'
351
// }
352
```
353
354
### Platform-Specific Package Resolution
355
356
Handling platform-specific conditions and files at the package level.
357
358
**Condition Names Configuration:**
359
360
```javascript
361
const context = {
362
unstable_conditionNames: ['import', 'require'],
363
unstable_conditionsByPlatform: {
364
ios: ['react-native', 'ios', 'native', 'import'],
365
android: ['react-native', 'android', 'native', 'import'],
366
web: ['browser', 'import']
367
}
368
};
369
```
370
371
**Platform-Specific Package Structure:**
372
373
```
374
package/
375
├── dist/
376
│ ├── index.js # Default
377
│ ├── index.native.js # React Native
378
│ ├── index.ios.js # iOS specific
379
│ └── index.web.js # Web specific
380
└── package.json
381
```