0
# Source Map Composition
1
2
Functionality for composing multiple source maps into a single source map, essential for multi-stage transformations in modern JavaScript toolchains.
3
4
## Capabilities
5
6
### Source Map Composition
7
8
Combine multiple source maps from a transformation pipeline into a single composite source map that maps directly from the final output back to the original source.
9
10
```javascript { .api }
11
/**
12
* Composes multiple source maps into a single source map
13
* Useful for multi-stage transformations where each stage produces its own source map
14
* @param maps - Array of source maps to compose, in transformation order
15
* @returns Single composed source map mapping final output to original sources
16
*/
17
function composeSourceMaps(maps: Array<MixedSourceMap>): MixedSourceMap;
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
const { composeSourceMaps } = require("metro-source-map");
24
25
// Example: TypeScript -> Babel -> Minifier transformation pipeline
26
const typeScriptMap = {
27
version: 3,
28
sources: ["src/app.ts"],
29
names: ["greeting"],
30
mappings: "AAAA,MAAM,QAAQ,GAAW,YAAY,CAAC",
31
sourcesContent: ["const greeting: string = 'hello';"]
32
};
33
34
const babelMap = {
35
version: 3,
36
sources: ["intermediate.js"],
37
names: ["greeting"],
38
mappings: "AAAA,IAAM,QAAQ,GAAG,YAAY,CAAC",
39
sourcesContent: ["var greeting = 'hello';"]
40
};
41
42
const minifierMap = {
43
version: 3,
44
sources: ["babel-output.js"],
45
names: ["a"],
46
mappings: "AAAA,IAAM,CAAC,CAAC,CAAC",
47
sourcesContent: ["var a='hello';"]
48
};
49
50
// Compose all transformations into a single map
51
// Final minified output -> Original TypeScript source
52
const composedMap = composeSourceMaps([
53
typeScriptMap, // TS -> JS
54
babelMap, // JS -> Babel JS
55
minifierMap // Babel JS -> Minified
56
]);
57
58
// The composed map now maps directly from minified code to original TypeScript
59
console.log(composedMap.sources); // ["src/app.ts"]
60
console.log(composedMap.sourcesContent[0]); // "const greeting: string = 'hello';"
61
```
62
63
### Multi-Stage Transformation
64
65
```javascript
66
const { composeSourceMaps, Generator } = require("metro-source-map");
67
68
// Example: Building a complex transformation pipeline
69
function createTransformationPipeline(originalCode, stages) {
70
const sourceMaps = [];
71
let currentCode = originalCode;
72
73
// Apply each transformation stage
74
stages.forEach((transform, index) => {
75
const result = transform(currentCode);
76
currentCode = result.code;
77
sourceMaps.push(result.map);
78
});
79
80
// Compose all source maps into one
81
const finalMap = composeSourceMaps(sourceMaps);
82
83
return {
84
code: currentCode,
85
map: finalMap
86
};
87
}
88
89
// Usage
90
const pipeline = createTransformationPipeline(originalTypeScript, [
91
typeScriptTransform,
92
babelTransform,
93
minifyTransform
94
]);
95
96
console.log(pipeline.code); // Final transformed code
97
console.log(pipeline.map.sources[0]); // Original source file
98
```
99
100
### Working with Different Source Map Types
101
102
```javascript
103
const { composeSourceMaps } = require("metro-source-map");
104
105
// Compose basic and indexed source maps
106
const basicMap1 = {
107
version: 3,
108
sources: ["input.js"],
109
names: ["fn"],
110
mappings: "AAAA,SAAS,EAAE"
111
};
112
113
const indexedMap = {
114
version: 3,
115
sections: [
116
{
117
offset: { line: 0, column: 0 },
118
map: {
119
version: 3,
120
sources: ["module1.js"],
121
names: ["helper"],
122
mappings: "AAAA,SAAS,MAAM"
123
}
124
}
125
]
126
};
127
128
const basicMap2 = {
129
version: 3,
130
sources: ["transformed.js"],
131
names: ["final"],
132
mappings: "AAAA,QAAQ,KAAK"
133
};
134
135
// Compose mixed source map types
136
const composed = composeSourceMaps([basicMap1, indexedMap, basicMap2]);
137
// Result will be a source map that traces back to the original sources
138
```
139
140
### Facebook and Hermes Extensions
141
142
The composition function preserves Facebook-specific and Hermes-specific extensions when composing source maps.
143
144
```javascript
145
const { composeSourceMaps } = require("metro-source-map");
146
147
// Source maps with Facebook extensions
148
const mapWithExtensions = {
149
version: 3,
150
sources: ["app.js"],
151
names: ["main"],
152
mappings: "AAAA",
153
x_facebook_sources: [
154
[{
155
names: ["functionA", "functionB"],
156
mappings: "AAAA,CAAC"
157
}]
158
],
159
x_hermes_function_offsets: {
160
0: [10, 20, 30]
161
},
162
x_metro_module_paths: ["src/app.js"]
163
};
164
165
const regularMap = {
166
version: 3,
167
sources: ["transformed.js"],
168
names: ["transformed"],
169
mappings: "AAAA"
170
};
171
172
// Compose maps preserving extensions
173
const composedWithExtensions = composeSourceMaps([
174
mapWithExtensions,
175
regularMap
176
]);
177
178
// Facebook/Hermes extensions are preserved in the result
179
console.log(composedWithExtensions.x_facebook_sources);
180
console.log(composedWithExtensions.x_hermes_function_offsets);
181
```
182
183
### Integration with Bundle Building
184
185
```javascript
186
const { composeSourceMaps, BundleBuilder } = require("metro-source-map");
187
188
// Use composition in bundle building workflows
189
function buildBundleWithComposition(modules) {
190
const builder = new BundleBuilder("bundle.js");
191
192
modules.forEach(module => {
193
// Each module might have its own transformation chain
194
const composedMap = composeSourceMaps(module.transformationMaps);
195
builder.append(module.code, composedMap);
196
});
197
198
return {
199
code: builder.getCode(),
200
map: builder.getMap()
201
};
202
}
203
204
// Usage
205
const bundle = buildBundleWithComposition([
206
{
207
code: "var utils = require('./utils');",
208
transformationMaps: [tsToJsMap, babelMap]
209
},
210
{
211
code: "var app = require('./app');",
212
transformationMaps: [tsToJsMap, babelMap, minifyMap]
213
}
214
]);
215
```
216
217
## Error Handling
218
219
Source map composition may encounter errors in the following cases:
220
221
- **Invalid source maps**: When input source maps are malformed or have invalid structure
222
- **Empty input**: When the maps array is empty or contains null/undefined values
223
- **Circular references**: When source maps reference each other in circular patterns
224
225
```javascript
226
const { composeSourceMaps } = require("metro-source-map");
227
228
try {
229
const composed = composeSourceMaps([validMap1, validMap2]);
230
} catch (error) {
231
if (error.message.includes('Invalid source map')) {
232
console.error('One of the input source maps is malformed:', error);
233
} else {
234
console.error('Source map composition failed:', error);
235
}
236
}
237
238
// Safe composition with validation
239
function safeComposeSourceMaps(maps) {
240
const validMaps = maps.filter(map =>
241
map && typeof map === 'object' && map.version === 3
242
);
243
244
if (validMaps.length === 0) {
245
return null;
246
}
247
248
if (validMaps.length === 1) {
249
return validMaps[0];
250
}
251
252
return composeSourceMaps(validMaps);
253
}
254
```