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
Additional utility functions for source map manipulation, conversion, and path normalization used throughout the Metro source map ecosystem.
Convert between different source map formats and segment representations.
/**
* Converts a standard source map object into Babel-compatible segments
* @param sourceMap - Basic source map object with mappings
* @returns Array of Babel source map segments for further processing
*/
function toBabelSegments(sourceMap: BasicSourceMap): Array<BabelSourceMapSegment>;
/**
* Converts a Babel source map segment into Metro's tuple format
* @param mapping - Babel source map segment object
* @returns Metro source map segment tuple (2, 4, or 5 elements)
*/
function toSegmentTuple(mapping: BabelSourceMapSegment): MetroSourceMapSegmentTuple;Usage Examples:
const { toBabelSegments, toSegmentTuple } = require("metro-source-map");
// Convert source map to Babel segments
const sourceMap = {
version: 3,
sources: ["app.js"],
names: ["console", "log"],
mappings: "AAAA,QAAQ,CAAC,GAAG",
sourcesContent: ["console.log('hello');"]
};
const babelSegments = toBabelSegments(sourceMap);
console.log(babelSegments);
// [
// {
// generated: { line: 1, column: 0 },
// original: { line: 1, column: 0 },
// source: "app.js",
// name: null
// },
// {
// generated: { line: 1, column: 8 },
// original: { line: 1, column: 8 },
// source: "app.js",
// name: "console"
// }
// ]
// Convert Babel segment back to Metro tuple
const segment = {
generated: { line: 1, column: 8 },
original: { line: 1, column: 8 },
source: "app.js",
name: "console"
};
const tuple = toSegmentTuple(segment);
console.log(tuple); // [1, 8, 1, 8, "console"]Normalize source paths for consistent source map references across different platforms and environments.
/**
* Normalizes source file paths for consistent source map references
* @param source - Source file path to normalize
* @param sourceRoot - Optional source root directory
* @returns Normalized source path
*/
function normalizeSourcePath(source: string, sourceRoot?: ?string): string;Usage Examples:
const { normalizeSourcePath } = require("metro-source-map");
// Basic path normalization
const normalized1 = normalizeSourcePath("./src/app.js");
console.log(normalized1); // "src/app.js"
// With source root
const normalized2 = normalizeSourcePath("../components/Button.js", "/project/src/");
console.log(normalized2); // "components/Button.js"
// Absolute path normalization
const normalized3 = normalizeSourcePath("/home/user/project/src/utils.js", "/home/user/project/");
console.log(normalized3); // "src/utils.js"
// Windows path normalization (when running on Windows)
const normalized4 = normalizeSourcePath("src\\components\\App.js");
console.log(normalized4); // "src/components/App.js"Types for working with different source map segment formats.
/**
* Babel source map segment format
*/
interface BabelSourceMapSegment {
/** Generated code position */
generated: {
line: number;
column: number;
};
/** Original source position (optional) */
original?: {
line: number;
column: number;
};
/** Source file path (optional) */
source?: ?string;
/** Symbol name (optional) */
name?: ?string;
}
/**
* Metro source map segment tuple formats
*/
type MetroSourceMapSegmentTuple =
| [number, number] // Generated position only
| [number, number, number, number] // Generated -> Original position
| [number, number, number, number, string]; // Generated -> Original position + nameconst { toBabelSegments, toSegmentTuple, normalizeSourcePath } = require("metro-source-map");
// Process source map with path normalization
function processSourceMapWithNormalizedPaths(sourceMap, sourceRoot) {
// Convert to Babel segments for processing
const segments = toBabelSegments(sourceMap);
// Normalize all source paths
const normalizedSegments = segments.map(segment => ({
...segment,
source: segment.source ? normalizeSourcePath(segment.source, sourceRoot) : segment.source
}));
// Convert back to Metro tuples
const tuples = normalizedSegments.map(toSegmentTuple);
return tuples;
}
// Usage
const sourceMap = {
version: 3,
sources: ["./src/app.js", "../utils/helper.js"],
names: ["main", "helper"],
mappings: "AAAA,SAAS,MAAM,CAAC,CAAC,CAAC"
};
const processedTuples = processSourceMapWithNormalizedPaths(sourceMap, "/project/");
console.log(processedTuples);const { toSegmentTuple, fromRawMappings, normalizeSourcePath } = require("metro-source-map");
// Convert external source map data to Metro format
function convertExternalSourceMap(externalMap) {
// External source map might use different segment format
const externalSegments = externalMap.segments; // Assume some external format
// Convert to Metro tuples
const metroTuples = externalSegments.map(segment => {
// Convert external format to Babel format first
const babelSegment = {
generated: { line: segment.genLine, column: segment.genCol },
original: segment.srcLine ? { line: segment.srcLine, column: segment.srcCol } : undefined,
source: segment.source ? normalizeSourcePath(segment.source) : undefined,
name: segment.name
};
// Convert to Metro tuple
return toSegmentTuple(babelSegment);
});
return metroTuples;
}
// Use converted tuples in Metro source map generation
function generateFromExternalMap(externalMap, moduleInfo) {
const metroTuples = convertExternalSourceMap(externalMap);
const modules = [{
map: metroTuples,
path: normalizeSourcePath(moduleInfo.path),
source: moduleInfo.source,
code: moduleInfo.code,
isIgnored: false
}];
return fromRawMappings(modules);
}const { normalizeSourcePath } = require("metro-source-map");
// Handle various path formats
const testPaths = [
"./src/app.js", // Relative path
"../components/Button.js", // Parent directory
"/absolute/path/file.js", // Absolute path
"simple.js", // Simple filename
"src\\windows\\path.js", // Windows path separators
"//network/share/file.js", // Network path
"C:\\Windows\\file.js", // Windows absolute
"" // Empty string
];
testPaths.forEach(path => {
const normalized = normalizeSourcePath(path, "/project/src/");
console.log(`${path} -> ${normalized}`);
});
// Custom normalization with validation
function safeNormalizeSourcePath(source, sourceRoot) {
try {
if (!source || typeof source !== 'string') {
return source;
}
return normalizeSourcePath(source, sourceRoot);
} catch (error) {
console.warn(`Path normalization failed for "${source}":`, error);
return source; // Return original path if normalization fails
}
}Utility functions may encounter errors in the following cases:
toBabelSegments receives malformed source map datatoSegmentTuple receives segments with missing required propertiesnormalizeSourcePath cannot resolve complex path structuresconst { toBabelSegments, toSegmentTuple, normalizeSourcePath } = require("metro-source-map");
// Safe conversion with error handling
function safeToBabelSegments(sourceMap) {
try {
return toBabelSegments(sourceMap);
} catch (error) {
console.error('Failed to convert source map to Babel segments:', error);
return [];
}
}
function safeToSegmentTuple(segment) {
try {
return toSegmentTuple(segment);
} catch (error) {
console.warn('Failed to convert segment to tuple:', error);
// Return a simple generated-only mapping as fallback
return [segment.generated.line, segment.generated.column];
}
}
// Robust path normalization
function robustNormalizeSourcePath(source, sourceRoot) {
if (!source || typeof source !== 'string') {
return source;
}
try {
return normalizeSourcePath(source, sourceRoot);
} catch (error) {
// Log the error but don't fail the entire process
console.debug(`Path normalization warning for "${source}":`, error.message);
return source;
}
}