0
# Configuration Merging
1
2
Deep merge multiple Metro configurations with validation and type safety.
3
4
## Capabilities
5
6
### Merge Configuration
7
8
Deeply merges multiple Metro configuration objects with intelligent handling of arrays, objects, and functions.
9
10
```javascript { .api }
11
/**
12
* Deep merges multiple Metro configuration objects
13
* @param defaultConfig - Base configuration object
14
* @param configs - Additional configurations to merge (later configs take precedence)
15
* @returns Complete merged Metro configuration
16
*/
17
function mergeConfig<T: $ReadOnly<InputConfigT>>(
18
defaultConfig: T,
19
...configs: Array<InputConfigT>
20
): T;
21
```
22
23
**Usage Examples:**
24
25
```javascript
26
import { getDefaultConfig, loadConfig, mergeConfig } from "metro-config";
27
28
// Merge user config with defaults
29
const defaultConfig = await getDefaultConfig();
30
const userConfig = await loadConfig();
31
const finalConfig = mergeConfig(defaultConfig, userConfig);
32
33
// Merge multiple configurations
34
const baseConfig = {
35
resolver: {
36
sourceExts: ['js', 'jsx'],
37
platforms: ['ios', 'android']
38
},
39
server: {
40
port: 8081
41
}
42
};
43
44
const platformConfig = {
45
resolver: {
46
sourceExts: ['js', 'jsx', 'ts', 'tsx'], // Replaces array
47
assetExts: ['png', 'jpg'] // Adds new property
48
}
49
};
50
51
const customConfig = {
52
transformer: {
53
babelTransformerPath: 'custom-transformer'
54
}
55
};
56
57
// Merge multiple configs - later configs take precedence
58
const merged = mergeConfig(baseConfig, platformConfig, customConfig);
59
// Result:
60
// {
61
// resolver: {
62
// sourceExts: ['js', 'jsx', 'ts', 'tsx'],
63
// platforms: ['ios', 'android'],
64
// assetExts: ['png', 'jpg']
65
// },
66
// server: {
67
// port: 8081
68
// },
69
// transformer: {
70
// babelTransformerPath: 'custom-transformer'
71
// }
72
// }
73
```
74
75
## Merge Behavior
76
77
### Object Properties
78
79
Objects are deeply merged, with properties from the second config taking precedence:
80
81
```javascript
82
const config1 = {
83
resolver: {
84
sourceExts: ['js'],
85
platforms: ['ios'],
86
blockList: /node_modules/
87
}
88
};
89
90
const config2 = {
91
resolver: {
92
sourceExts: ['js', 'ts'], // Overrides
93
assetExts: ['png'] // Adds new property
94
// platforms and blockList preserved from config1
95
}
96
};
97
98
const merged = mergeConfig(config1, config2);
99
// resolver.sourceExts = ['js', 'ts']
100
// resolver.platforms = ['ios'] (preserved)
101
// resolver.assetExts = ['png'] (added)
102
// resolver.blockList = /node_modules/ (preserved)
103
```
104
105
### Array Properties
106
107
Arrays from the second configuration completely replace arrays from the first:
108
109
```javascript
110
const config1 = {
111
resolver: {
112
sourceExts: ['js', 'jsx']
113
}
114
};
115
116
const config2 = {
117
resolver: {
118
sourceExts: ['ts', 'tsx'] // Completely replaces
119
}
120
};
121
122
const merged = mergeConfig(config1, config2);
123
// resolver.sourceExts = ['ts', 'tsx'] (not merged)
124
```
125
126
### Function Properties
127
128
Functions from the second configuration replace functions from the first:
129
130
```javascript
131
const config1 = {
132
transformer: {
133
getTransformOptions: async () => ({
134
transform: { inlineRequires: false }
135
})
136
}
137
};
138
139
const config2 = {
140
transformer: {
141
getTransformOptions: async () => ({
142
transform: { inlineRequires: true }
143
})
144
}
145
};
146
147
const merged = mergeConfig(config1, config2);
148
// Uses config2's getTransformOptions function
149
```
150
151
### Primitive Properties
152
153
Primitive values (strings, numbers, booleans) from the second config override the first:
154
155
```javascript
156
const config1 = {
157
server: {
158
port: 8081,
159
forwardClientLogs: false
160
}
161
};
162
163
const config2 = {
164
server: {
165
port: 3000 // Overrides
166
// forwardClientLogs preserved from config1
167
}
168
};
169
170
const merged = mergeConfig(config1, config2);
171
// server.port = 3000
172
// server.forwardClientLogs = false (preserved)
173
```
174
175
## Advanced Merging Patterns
176
177
### Extending Array Configuration
178
179
To extend rather than replace arrays, pre-process your configuration:
180
181
```javascript
182
import { getDefaultConfig, mergeConfig } from "metro-config";
183
184
async function createExtendedConfig(userConfig) {
185
const defaultConfig = await getDefaultConfig();
186
187
// Extend sourceExts rather than replace
188
const extendedConfig = {
189
...userConfig,
190
resolver: {
191
...userConfig.resolver,
192
sourceExts: [
193
...defaultConfig.resolver.sourceExts,
194
...(userConfig.resolver?.sourceExts || [])
195
]
196
}
197
};
198
199
return mergeConfig(defaultConfig, extendedConfig);
200
}
201
```
202
203
### Conditional Configuration Merging
204
205
Handle environment-specific configurations:
206
207
```javascript
208
import { getDefaultConfig, mergeConfig } from "metro-config";
209
210
async function createConfig() {
211
const baseConfig = await getDefaultConfig();
212
213
const developmentConfig = {
214
server: {
215
port: 8081,
216
forwardClientLogs: true
217
},
218
resetCache: true
219
};
220
221
const productionConfig = {
222
transformer: {
223
minifierPath: 'metro-minify-terser',
224
minifierConfig: {
225
mangle: { toplevel: true },
226
compress: { drop_console: true }
227
}
228
}
229
};
230
231
const envConfig = process.env.NODE_ENV === 'production'
232
? productionConfig
233
: developmentConfig;
234
235
return mergeConfig(baseConfig, envConfig);
236
}
237
```
238
239
### Function Configuration Merging
240
241
Compose transform functions when merging:
242
243
```javascript
244
const baseConfig = {
245
transformer: {
246
getTransformOptions: async (entryPoints, options) => ({
247
transform: {
248
experimentalImportSupport: false,
249
inlineRequires: false
250
}
251
})
252
}
253
};
254
255
const enhancedConfig = {
256
transformer: {
257
getTransformOptions: async (entryPoints, options) => {
258
// Get base transform options
259
const baseTransform = await baseConfig.transformer.getTransformOptions(
260
entryPoints,
261
options
262
);
263
264
// Enhance with additional options
265
return {
266
...baseTransform,
267
transform: {
268
...baseTransform.transform,
269
inlineRequires: true // Override specific option
270
},
271
preloadedModules: false
272
};
273
}
274
}
275
};
276
277
const merged = mergeConfig(baseConfig, enhancedConfig);
278
```
279
280
## Validation After Merging
281
282
The merged configuration is automatically validated:
283
284
```javascript
285
import { mergeConfig } from "metro-config";
286
287
try {
288
const merged = mergeConfig(config1, config2);
289
// Configuration is valid and ready to use
290
} catch (error) {
291
if (error.name === 'ValidationError') {
292
console.error('Merged configuration is invalid:', error.message);
293
// Handle validation errors
294
}
295
}
296
```
297
298
## Type Safety
299
300
When using TypeScript, the merge function provides type safety:
301
302
```typescript
303
import type { InputConfigT } from 'metro-config';
304
import { mergeConfig } from 'metro-config';
305
306
const config1: InputConfigT = {
307
resolver: {
308
sourceExts: ['js', 'jsx']
309
}
310
};
311
312
const config2: InputConfigT = {
313
resolver: {
314
sourceExts: ['ts', 'tsx'],
315
// TypeScript ensures all properties are valid
316
}
317
};
318
319
const merged = mergeConfig(config1, config2);
320
// merged has complete ConfigT type
321
```