CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-source-map

Generates and consumes source maps for debugging tools that map minified code back to original source code

Pending
Overview
Eval results
Files

source-map-consumption.mddocs/

Source Map Consumption

The SourceMapConsumer classes provide functionality for reading and querying existing source maps. This enables bidirectional position mapping, source content access, and mapping iteration for debugging tools, development servers, and error reporting systems.

Capabilities

SourceMapConsumer Constructor

Creates consumers for both regular and indexed source maps with automatic type detection.

/**
 * Creates a new SourceMapConsumer from raw source map data
 * @param rawSourceMap - Raw source map object, indexed map, or JSON string
 * @param sourceMapUrl - Optional URL of the source map for resolving relative paths
 * @returns Promise resolving to BasicSourceMapConsumer or IndexedSourceMapConsumer
 */
interface SourceMapConsumerConstructor {
  new (
    rawSourceMap: RawSourceMap | RawIndexMap | string,
    sourceMapUrl?: SourceMapUrl
  ): Promise<BasicSourceMapConsumer | IndexedSourceMapConsumer>;
  
  /**
   * Initialize WASM for browser usage (required before creating consumers)
   * @param mappings - Object containing WASM file URL or ArrayBuffer
   */
  initialize(mappings: SourceMappings): void;
  
  /**
   * Create a BasicSourceMapConsumer from a SourceMapGenerator
   * @param sourceMap - The source map generator to consume
   * @param sourceMapUrl - Optional URL for resolving relative paths
   */
  fromSourceMap(
    sourceMap: SourceMapGenerator,
    sourceMapUrl?: SourceMapUrl
  ): Promise<BasicSourceMapConsumer>;
  
  /**
   * Auto-cleanup wrapper that ensures destroy() is called
   * @param rawSourceMap - Raw source map data
   * @param sourceMapUrl - Optional source map URL
   * @param callback - Function to execute with the consumer
   */
  with<T>(
    rawSourceMap: RawSourceMap | RawIndexMap | string,
    sourceMapUrl: SourceMapUrl | null | undefined,
    callback: (consumer: BasicSourceMapConsumer | IndexedSourceMapConsumer) => Promise<T> | T
  ): Promise<T>;
}

interface SourceMappings {
  "lib/mappings.wasm": SourceMapUrl | ArrayBuffer;
}

type SourceMapUrl = string;

Usage Examples:

import { SourceMapConsumer } from "source-map";

// Browser initialization (required)
SourceMapConsumer.initialize({
  "lib/mappings.wasm": "https://unpkg.com/source-map@0.7.6/lib/mappings.wasm"
});

// Create consumer (auto-detects regular vs indexed)
const consumer = await new SourceMapConsumer(rawSourceMap);

// Recommended: Use auto-cleanup wrapper
const result = await SourceMapConsumer.with(rawSourceMap, null, async (consumer) => {
  const originalPos = consumer.originalPositionFor({ line: 1, column: 12 });
  return originalPos;
  // consumer.destroy() called automatically
});

Basic Source Map Consumer

Handles regular source maps with full position mapping and source content access.

interface BasicSourceMapConsumer extends SourceMapConsumer {
  /** Generated file name */
  readonly file: string;
  /** Source root URL */  
  readonly sourceRoot: string;
  /** Array of absolute source URLs */
  readonly sources: string[];
  /** Array of source file contents */
  readonly sourcesContent: string[];
}

interface BasicSourceMapConsumerConstructor {
  new (rawSourceMap: RawSourceMap | string): Promise<BasicSourceMapConsumer>;
  fromSourceMap(sourceMap: SourceMapGenerator): Promise<BasicSourceMapConsumer>;
}

Indexed Source Map Consumer

Handles indexed source maps containing multiple sections for large applications.

interface IndexedSourceMapConsumer extends SourceMapConsumer {
  /** Combined sources from all sections */
  readonly sources: string[];
}

interface IndexedSourceMapConsumerConstructor {
  new (rawSourceMap: RawIndexMap | string): Promise<IndexedSourceMapConsumer>;
}

Position Mapping

Map between generated and original code positions bidirectionally.

/**
 * Returns the original source, line, and column information for the generated
 * source's line and column positions provided
 * @param generatedPosition - Position in generated code with optional bias
 * @returns Original position information (values can be null if not found)
 */
originalPositionFor(
  generatedPosition: Position & { bias?: number }
): NullableMappedPosition;

/**
 * Returns the generated line and column information for the original source,
 * line, and column positions provided
 * @param originalPosition - Position in original code with optional bias  
 * @returns Generated position information (values can be null if not found)
 */
generatedPositionFor(
  originalPosition: MappedPosition & { bias?: number }
): NullablePosition;

/**
 * Returns all generated line and column information for the original source,
 * line, and column provided
 * @param originalPosition - Position in original code
 * @returns Array of all corresponding generated positions
 */
allGeneratedPositionsFor(
  originalPosition: MappedPosition
): NullablePosition[];

interface Position {
  line: number;    // 1-based line number
  column: number;  // 0-based column number
}

interface MappedPosition {
  source: string;   // Original source file name
  line: number;     // 1-based line number
  column: number;   // 0-based column number
  name?: string;    // Optional original identifier name
}

interface NullablePosition {
  line: number | null;
  column: number | null;
  lastColumn: number | null;
}

interface NullableMappedPosition {
  source: string | null;
  line: number | null;
  column: number | null;
  name: string | null;
}

Usage Examples:

// Find original position for generated code
const originalPos = consumer.originalPositionFor({
  line: 1,      // Generated line (1-based)
  column: 28    // Generated column (0-based)
});
console.log(originalPos);
// { source: 'math.js', line: 15, column: 4, name: 'add' }

// Find generated position for original code  
const generatedPos = consumer.generatedPositionFor({
  source: 'math.js',
  line: 15,
  column: 4
});
console.log(generatedPos);
// { line: 1, column: 28, lastColumn: 32 }

// Get all generated positions for original
const allPositions = consumer.allGeneratedPositionsFor({
  source: 'utils.js',
  line: 10,
  column: 0
});
console.log(allPositions);
// [{ line: 5, column: 12, lastColumn: null }, { line: 8, column: 0, lastColumn: null }]

Search Bias Constants

Control position lookup behavior when exact matches aren't found.

// Static constants on SourceMapConsumer
static readonly GREATEST_LOWER_BOUND: 1;  // Find closest smaller element
static readonly LEAST_UPPER_BOUND: 2;     // Find closest larger element

// Iteration order constants  
static readonly GENERATED_ORDER: 1;       // Sort by generated position
static readonly ORIGINAL_ORDER: 2;        // Sort by original position

Usage:

// Use bias for fuzzy position matching
const originalPos = consumer.originalPositionFor({
  line: 1,
  column: 15,
  bias: SourceMapConsumer.GREATEST_LOWER_BOUND  // Find closest match <= position
});

Source Content Access

Access original source file contents embedded in the source map.

/**
 * Return true if we have the source content for every source in the source
 * map, false otherwise
 */
hasContentsOfAllSources(): boolean;

/**
 * Returns the original source content. The only argument is the url of the
 * original source file. Returns null if no original source content is available
 * @param source - Source file name/URL
 * @param returnNullOnMissing - If true, return null instead of throwing error
 */
sourceContentFor(source: string, returnNullOnMissing?: boolean): string | null;

Usage:

// Check if all source content is available
if (consumer.hasContentsOfAllSources()) {
  console.log("All source content is embedded");
}

// Get content for specific source
const content = consumer.sourceContentFor("math.js");
if (content) {
  console.log("Source content:", content);
} else {
  console.log("No content available for math.js");
}

// Safe content access without exceptions
const safeContent = consumer.sourceContentFor("missing.js", true);
console.log(safeContent); // null if not found

Mapping Iteration

Iterate over all mappings in the source map with different ordering options.

/**
 * Iterate over each mapping between an original source/line/column and a
 * generated line/column in this source map
 * @param callback - Function called with each mapping
 * @param context - Optional context object for callback
 * @param order - Iteration order (GENERATED_ORDER or ORIGINAL_ORDER)
 */
eachMapping(
  callback: (mapping: MappingItem) => void,
  context?: any,
  order?: number
): void;


interface MappingItem {
  source: string;
  generatedLine: number;
  generatedColumn: number;
  lastGeneratedColumn: number | null;
  originalLine: number;
  originalColumn: number;
  name: string;
}

Usage:

// Iterate in generated code order
consumer.eachMapping((mapping) => {
  console.log(`Generated ${mapping.generatedLine}:${mapping.generatedColumn} -> ` +
              `Original ${mapping.source}:${mapping.originalLine}:${mapping.originalColumn}`);
}, null, SourceMapConsumer.GENERATED_ORDER);

// Iterate in original source order
consumer.eachMapping((mapping) => {
  console.log(`${mapping.source}:${mapping.originalLine}:${mapping.originalColumn} -> ` +
              `${mapping.generatedLine}:${mapping.generatedColumn}`);
}, null, SourceMapConsumer.ORIGINAL_ORDER);

// Use context object
const stats = { totalMappings: 0, sources: new Set() };
consumer.eachMapping(function(mapping) {
  this.totalMappings++;
  this.sources.add(mapping.source);
}, stats);
console.log(`Found ${stats.totalMappings} mappings across ${stats.sources.size} sources`);

Column Spans

Calculate ending column positions for mappings.

/**
 * Compute the last column for each generated mapping. The last column is
 * inclusive.
 */
computeColumnSpans(): void;

Usage:

// Calculate column spans for better range mapping
consumer.computeColumnSpans();

// Now lastColumn will be populated in position results
const pos = consumer.originalPositionFor({ line: 1, column: 0 });
console.log(`Mapping spans from column ${pos.column} to ${pos.lastColumn}`);

Resource Management

Properly clean up WASM resources to prevent memory leaks.

/**
 * Free this source map consumer's associated wasm data that is manually-managed.
 * Alternatively, you can use SourceMapConsumer.with to avoid needing to remember to call destroy.
 */
destroy(): void;

Usage:

// Manual cleanup (not recommended)
const consumer = await new SourceMapConsumer(rawSourceMap);
try {
  // Use consumer
  const pos = consumer.originalPositionFor({ line: 1, column: 0 });
} finally {
  consumer.destroy(); // Required to prevent memory leaks
}

// Recommended: automatic cleanup
const result = await SourceMapConsumer.with(rawSourceMap, null, (consumer) => {
  return consumer.originalPositionFor({ line: 1, column: 0 });
  // destroy() called automatically
});

Data Types

interface RawSourceMap {
  version: number;
  sources: string[];
  names: string[];
  sourceRoot?: string;
  sourcesContent?: string[];
  mappings: string;
  file: string;
  x_google_ignoreList?: number[];  // Indices of third-party sources to ignore in Chrome DevTools
}

interface RawIndexMap {
  version: number;
  sections: RawSection[];
  file?: string;
  sourceRoot?: string;
}

interface RawSection {
  offset: Position;
  map: RawSourceMap;
}

Error Handling

Source map consumption can fail for various reasons:

  • Unsupported version: Only version 3 source maps are supported
  • Invalid format: Malformed JSON or missing required fields
  • WASM not initialized: Browser usage requires SourceMapConsumer.initialize()
  • Source not found: Requested source file not present in source map
  • Invalid positions: Line/column values outside valid ranges
try {
  const consumer = await new SourceMapConsumer(malformedSourceMap);
} catch (error) {
  if (error.message.includes("Unsupported version")) {
    console.error("Source map version not supported");
  } else if (error.message.includes("lib/mappings.wasm")) {
    console.error("WASM not initialized for browser usage");
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-source-map

docs

index.md

source-map-consumption.md

source-map-generation.md

source-node-building.md

tile.json