0
# Module Resolution
1
2
The Resolver plugin enables custom module resolution logic for import and require statements. Resolvers determine how module specifiers are resolved to actual file paths and provide the foundation for custom module systems.
3
4
## Capabilities
5
6
### Resolver Class
7
8
Base class for creating module resolution plugins.
9
10
```typescript { .api }
11
/**
12
* Base class for module resolution plugins
13
* @template T - Configuration type for this resolver
14
*/
15
export declare class Resolver<T> {
16
constructor(opts: ResolverOpts<T>);
17
}
18
19
/**
20
* Resolver plugin configuration interface
21
* @template ConfigType - Type of configuration returned by loadConfig
22
*/
23
interface ResolverOpts<ConfigType> {
24
/** Load configuration for this resolver */
25
loadConfig?: (args: {
26
config: Config;
27
options: PluginOptions;
28
logger: PluginLogger;
29
tracer: PluginTracer;
30
}) => Promise<ConfigType> | ConfigType;
31
32
/** Resolve a module specifier to a file path (required) */
33
resolve(args: {
34
dependency: Dependency;
35
options: PluginOptions;
36
logger: PluginLogger;
37
tracer: PluginTracer;
38
specifier: FilePath;
39
pipeline?: string;
40
config: ConfigType;
41
}): Promise<ResolveResult | null>;
42
}
43
```
44
45
**Usage Example:**
46
47
```javascript
48
import { Resolver } from "@parcel/plugin";
49
import path from "path";
50
51
export default new Resolver({
52
// Load resolver configuration
53
loadConfig({config}) {
54
return config.getConfigFrom(/* config path */);
55
},
56
57
// Main resolution logic (required)
58
async resolve({dependency, specifier, options, logger}) {
59
// Handle different types of specifiers
60
if (specifier.startsWith('~')) {
61
// Resolve from node_modules
62
const modulePath = specifier.slice(1);
63
const resolved = path.resolve(options.projectRoot, 'node_modules', modulePath);
64
65
return {
66
filePath: resolved,
67
sideEffects: false
68
};
69
}
70
71
if (specifier.startsWith('./') || specifier.startsWith('../')) {
72
// Relative path resolution
73
const fromDir = path.dirname(dependency.sourcePath);
74
const resolved = path.resolve(fromDir, specifier);
75
76
return {
77
filePath: resolved,
78
sideEffects: true
79
};
80
}
81
82
// Return null to let other resolvers handle this
83
return null;
84
}
85
});
86
```
87
88
### Resolution Results
89
90
```typescript { .api }
91
/**
92
* Result from a module resolution operation
93
*/
94
interface ResolveResult {
95
/** Resolved file path */
96
filePath: FilePath;
97
98
/** Whether this module has side effects */
99
sideEffects?: boolean;
100
101
/** Module resolution priority */
102
priority?: ResolvePriority;
103
104
/** Whether this is an excluded dependency */
105
isExcluded?: boolean;
106
107
/** Code to inline instead of resolving */
108
code?: string;
109
110
/** Query parameters for the resolved module */
111
query?: URLSearchParams;
112
113
/** Pipeline to use for processing this module */
114
pipeline?: string;
115
116
/** Environment for this module */
117
env?: EnvironmentOptions;
118
119
/** Module metadata */
120
meta?: Record<string, any>;
121
122
/** Symbol exports from this module */
123
symbols?: Map<Symbol, SymbolResolution>;
124
}
125
126
/**
127
* Resolution priority levels
128
*/
129
type ResolvePriority = "sync" | "parallel" | "lazy";
130
```
131
132
### Dependency Information
133
134
```typescript { .api }
135
/**
136
* Dependency being resolved
137
*/
138
interface Dependency {
139
/** Module specifier to resolve */
140
specifier: string;
141
142
/** Source file path containing this dependency */
143
sourcePath: FilePath;
144
145
/** Source location of the dependency */
146
loc?: SourceLocation;
147
148
/** Import type */
149
specifierType: SpecifierType;
150
151
/** Whether this is an optional dependency */
152
isOptional?: boolean;
153
154
/** Whether this dependency is async */
155
isAsync?: boolean;
156
157
/** Dependency priority */
158
priority?: DependencyPriority;
159
160
/** Bundle behavior for this dependency */
161
bundleBehavior?: BundleBehavior;
162
163
/** Environment for this dependency */
164
env?: EnvironmentOptions;
165
166
/** Dependency metadata */
167
meta?: Record<string, any>;
168
169
/** Pipeline to use for this dependency */
170
pipeline?: string;
171
172
/** Symbols imported from this dependency */
173
symbols?: Map<Symbol, SymbolResolution>;
174
}
175
176
/**
177
* Types of module specifiers
178
*/
179
type SpecifierType =
180
| "commonjs"
181
| "esm"
182
| "url"
183
| "relative"
184
| "absolute";
185
186
/**
187
* Dependency loading priority
188
*/
189
type DependencyPriority = "sync" | "parallel" | "lazy";
190
191
/**
192
* Bundle inclusion behavior
193
*/
194
type BundleBehavior =
195
| "inline"
196
| "isolated";
197
```
198
199
### Source Location Information
200
201
```typescript { .api }
202
/**
203
* Location information in source code
204
*/
205
interface SourceLocation {
206
/** Source file path */
207
filePath: FilePath;
208
209
/** Start position */
210
start: Position;
211
212
/** End position */
213
end: Position;
214
}
215
216
/**
217
* Position in source code
218
*/
219
interface Position {
220
/** Line number (1-based) */
221
line: number;
222
223
/** Column number (0-based) */
224
column: number;
225
}
226
```
227
228
### Common Resolution Patterns
229
230
**Package Resolution:**
231
```javascript
232
// Resolve from node_modules
233
if (!specifier.startsWith('.')) {
234
const packagePath = await options.packageManager.resolve(specifier, fromPath);
235
return { filePath: packagePath };
236
}
237
```
238
239
**Alias Resolution:**
240
```javascript
241
// Handle path aliases
242
const aliases = config.aliases || {};
243
for (const [alias, target] of Object.entries(aliases)) {
244
if (specifier.startsWith(alias)) {
245
const resolved = specifier.replace(alias, target);
246
return { filePath: path.resolve(options.projectRoot, resolved) };
247
}
248
}
249
```
250
251
**Extension Resolution:**
252
```javascript
253
// Try different file extensions
254
const extensions = ['.js', '.ts', '.jsx', '.tsx'];
255
for (const ext of extensions) {
256
const withExt = specifier + ext;
257
if (await options.inputFS.exists(withExt)) {
258
return { filePath: withExt };
259
}
260
}
261
```