Source map generator for Metro bundler with advanced mapping capabilities and Facebook/Hermes extensions.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Functionality for composing multiple source maps into a single source map, essential for multi-stage transformations in modern JavaScript toolchains.
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.
/**
* Composes multiple source maps into a single source map
* Useful for multi-stage transformations where each stage produces its own source map
* @param maps - Array of source maps to compose, in transformation order
* @returns Single composed source map mapping final output to original sources
*/
function composeSourceMaps(maps: Array<MixedSourceMap>): MixedSourceMap;Usage Examples:
const { composeSourceMaps } = require("metro-source-map");
// Example: TypeScript -> Babel -> Minifier transformation pipeline
const typeScriptMap = {
version: 3,
sources: ["src/app.ts"],
names: ["greeting"],
mappings: "AAAA,MAAM,QAAQ,GAAW,YAAY,CAAC",
sourcesContent: ["const greeting: string = 'hello';"]
};
const babelMap = {
version: 3,
sources: ["intermediate.js"],
names: ["greeting"],
mappings: "AAAA,IAAM,QAAQ,GAAG,YAAY,CAAC",
sourcesContent: ["var greeting = 'hello';"]
};
const minifierMap = {
version: 3,
sources: ["babel-output.js"],
names: ["a"],
mappings: "AAAA,IAAM,CAAC,CAAC,CAAC",
sourcesContent: ["var a='hello';"]
};
// Compose all transformations into a single map
// Final minified output -> Original TypeScript source
const composedMap = composeSourceMaps([
typeScriptMap, // TS -> JS
babelMap, // JS -> Babel JS
minifierMap // Babel JS -> Minified
]);
// The composed map now maps directly from minified code to original TypeScript
console.log(composedMap.sources); // ["src/app.ts"]
console.log(composedMap.sourcesContent[0]); // "const greeting: string = 'hello';"const { composeSourceMaps, Generator } = require("metro-source-map");
// Example: Building a complex transformation pipeline
function createTransformationPipeline(originalCode, stages) {
const sourceMaps = [];
let currentCode = originalCode;
// Apply each transformation stage
stages.forEach((transform, index) => {
const result = transform(currentCode);
currentCode = result.code;
sourceMaps.push(result.map);
});
// Compose all source maps into one
const finalMap = composeSourceMaps(sourceMaps);
return {
code: currentCode,
map: finalMap
};
}
// Usage
const pipeline = createTransformationPipeline(originalTypeScript, [
typeScriptTransform,
babelTransform,
minifyTransform
]);
console.log(pipeline.code); // Final transformed code
console.log(pipeline.map.sources[0]); // Original source fileconst { composeSourceMaps } = require("metro-source-map");
// Compose basic and indexed source maps
const basicMap1 = {
version: 3,
sources: ["input.js"],
names: ["fn"],
mappings: "AAAA,SAAS,EAAE"
};
const indexedMap = {
version: 3,
sections: [
{
offset: { line: 0, column: 0 },
map: {
version: 3,
sources: ["module1.js"],
names: ["helper"],
mappings: "AAAA,SAAS,MAAM"
}
}
]
};
const basicMap2 = {
version: 3,
sources: ["transformed.js"],
names: ["final"],
mappings: "AAAA,QAAQ,KAAK"
};
// Compose mixed source map types
const composed = composeSourceMaps([basicMap1, indexedMap, basicMap2]);
// Result will be a source map that traces back to the original sourcesThe composition function preserves Facebook-specific and Hermes-specific extensions when composing source maps.
const { composeSourceMaps } = require("metro-source-map");
// Source maps with Facebook extensions
const mapWithExtensions = {
version: 3,
sources: ["app.js"],
names: ["main"],
mappings: "AAAA",
x_facebook_sources: [
[{
names: ["functionA", "functionB"],
mappings: "AAAA,CAAC"
}]
],
x_hermes_function_offsets: {
0: [10, 20, 30]
},
x_metro_module_paths: ["src/app.js"]
};
const regularMap = {
version: 3,
sources: ["transformed.js"],
names: ["transformed"],
mappings: "AAAA"
};
// Compose maps preserving extensions
const composedWithExtensions = composeSourceMaps([
mapWithExtensions,
regularMap
]);
// Facebook/Hermes extensions are preserved in the result
console.log(composedWithExtensions.x_facebook_sources);
console.log(composedWithExtensions.x_hermes_function_offsets);const { composeSourceMaps, BundleBuilder } = require("metro-source-map");
// Use composition in bundle building workflows
function buildBundleWithComposition(modules) {
const builder = new BundleBuilder("bundle.js");
modules.forEach(module => {
// Each module might have its own transformation chain
const composedMap = composeSourceMaps(module.transformationMaps);
builder.append(module.code, composedMap);
});
return {
code: builder.getCode(),
map: builder.getMap()
};
}
// Usage
const bundle = buildBundleWithComposition([
{
code: "var utils = require('./utils');",
transformationMaps: [tsToJsMap, babelMap]
},
{
code: "var app = require('./app');",
transformationMaps: [tsToJsMap, babelMap, minifyMap]
}
]);Source map composition may encounter errors in the following cases:
const { composeSourceMaps } = require("metro-source-map");
try {
const composed = composeSourceMaps([validMap1, validMap2]);
} catch (error) {
if (error.message.includes('Invalid source map')) {
console.error('One of the input source maps is malformed:', error);
} else {
console.error('Source map composition failed:', error);
}
}
// Safe composition with validation
function safeComposeSourceMaps(maps) {
const validMaps = maps.filter(map =>
map && typeof map === 'object' && map.version === 3
);
if (validMaps.length === 0) {
return null;
}
if (validMaps.length === 1) {
return validMaps[0];
}
return composeSourceMaps(validMaps);
}