CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-metro-source-map

Source map generator for Metro bundler with advanced mapping capabilities and Facebook/Hermes extensions.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

bundle-building.mddocs/

Bundle Building

Functionality for building source-mapped bundles by concatenating strings and their corresponding source maps, producing indexed source maps for efficient debugging.

Capabilities

BundleBuilder Class

Main class for building bundles with source maps by concatenating code sections and composing their source maps.

/**
 * Builds a source-mapped bundle by concatenating strings and their source maps
 */
class BundleBuilder {
  /**
   * Creates a new bundle builder
   * @param file - Name of the output bundle file
   */
  constructor(file: string);
  
  /**
   * Append code with optional source map to the bundle
   * @param code - Code string to append
   * @param map - Optional source map for the code
   * @returns Self for method chaining
   */
  append(code: string, map?: MixedSourceMap): this;
  
  /**
   * Get the concatenated code for the bundle
   * @returns Complete bundle code
   */
  getCode(): string;
  
  /**
   * Get the composed source map for the bundle
   * @returns Indexed source map covering the entire bundle
   */
  getMap(): MixedSourceMap;
}

Usage Examples:

const { BundleBuilder } = require("metro-source-map");

// Create a bundle builder
const builder = new BundleBuilder("bundle.js");

// Append code sections with their source maps
builder
  .append("// Header comment\n")  // No source map
  .append("console.log('module1');\n", module1SourceMap)
  .append("console.log('module2');\n", module2SourceMap)
  .append("// Footer\n");  // No source map

// Get the final bundle
const bundleCode = builder.getCode();
const bundleMap = builder.getMap();

console.log(bundleCode);
// Output:
// // Header comment
// console.log('module1');
// console.log('module2');
// // Footer

console.log(bundleMap.sections.length); // Number of source map sections

Index Map Creation

Utility function for creating empty indexed source maps.

/**
 * Creates an indexed source map from file and sections
 * @param file - File name for the source map
 * @param sections - Array of source map sections with offsets
 * @returns Indexed source map structure
 */
function createIndexMap(file: string, sections: Array<IndexMapSection>): IndexMap;

Usage Examples:

const { createIndexMap } = require("metro-source-map");

// Create an index map with sections
const sections = [
  {
    offset: { line: 0, column: 0 },
    map: someBasicSourceMap
  }
];
const indexMap = createIndexMap("output.js", sections);
console.log(indexMap);
// {
//   version: 3,
//   sections: [...],
//   file: "output.js"
// }

// Use with BundleBuilder for custom initialization
const builder = new BundleBuilder("custom.js");
// Builder internally uses createIndexMap

Advanced Bundle Building

const { BundleBuilder } = require("metro-source-map");

// Building a complex bundle with mixed content types
const builder = new BundleBuilder("app.bundle.js");

// Add a header without source map
builder.append("(function() {\n");

// Add main application modules with source maps
const modules = [
  { code: "var utils = require('./utils');\n", map: utilsSourceMap },
  { code: "var app = require('./app');\n", map: appSourceMap },
  { code: "app.start();\n", map: startSourceMap }
];

modules.forEach(({ code, map }) => {
  builder.append(code, map);
});

// Add a footer without source map
builder.append("})();\n");

// Generate the final bundle
const result = {
  code: builder.getCode(),
  map: builder.getMap()
};

// The resulting map will be an indexed source map with sections
// corresponding to each piece of code that had an associated source map
console.log(result.map.sections.length); // Number of mapped sections

Index Map Structure

/**
 * Indexed source map format for bundles
 */
interface IndexMap {
  /** Source map version (always 3) */
  version: number;
  /** Output file name */
  file?: string;
  /** Array of source map sections */
  sections: Array<IndexMapSection>;
  /** Facebook extension: byte offsets */
  x_facebook_offsets?: Array<number>;
  /** Metro extension: module paths */
  x_metro_module_paths?: Array<string>;
  /** Facebook extension: segment map */
  x_facebook_segments?: FBSegmentMap;
  /** Hermes extension: function offsets */
  x_hermes_function_offsets?: HermesFunctionOffsets;
  /** These fields are void in IndexMap to maintain type safety */
  mappings?: void;
  sourcesContent?: void;
  x_facebook_sources?: void;
  x_google_ignoreList?: void;
}

/**
 * Individual section within an indexed source map
 */
interface IndexMapSection {
  /** Source map for this section (can be basic or indexed) */
  map: IndexMap | BasicSourceMap;
  /** Offset where this section starts in the bundle */
  offset: {
    /** Line offset (0-based) */
    line: number;
    /** Column offset (0-based) */  
    column: number;
  };
}

Integration with Source Map Types

const { BundleBuilder, Generator, fromRawMappings } = require("metro-source-map");

// Building a bundle using generated source maps
const builder = new BundleBuilder("output.js");

// Generate source maps for individual modules
const module1Map = fromRawMappings([{
  map: [[1, 0, 1, 0], [1, 8, 1, 8]],
  path: "src/module1.js",
  source: "console.log('one');",
  code: "console.log('one');",
  isIgnored: false
}]).toMap();

const module2Map = fromRawMappings([{
  map: [[1, 0, 1, 0], [1, 8, 1, 8]],
  path: "src/module2.js", 
  source: "console.log('two');",
  code: "console.log('two');",
  isIgnored: false
}]).toMap();

// Build the bundle
builder
  .append("console.log('one');\n", module1Map)
  .append("console.log('two');\n", module2Map);

const finalMap = builder.getMap();
// finalMap will be an IndexMap with 2 sections

Error Handling

BundleBuilder operations may encounter errors in the following cases:

  • Invalid source maps: When provided source maps are malformed
  • Empty builder: When getMap() or getCode() is called before any content is appended
const { BundleBuilder } = require("metro-source-map");

try {
  const builder = new BundleBuilder("test.js");
  builder.append("console.log('test');", invalidSourceMap);
  const map = builder.getMap();
} catch (error) {
  console.error('Bundle building failed:', error);
}

// Safe usage with validation
const builder = new BundleBuilder("safe.js");
if (someCode && someCode.length > 0) {
  builder.append(someCode, optionalSourceMap);
}

docs

bundle-building.md

composition.md

consumption.md

function-maps.md

generation.md

index.md

utilities.md

tile.json