# How SWC Handles Source Maps and Debugging Support: A Deep Dive into the Architecture

> Discover how SWC handles source maps and debugging. Explore its centralized SourceMap architecture, byte-to-line conversion, V3 source map emission, and WebAssembly plugin interface.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: deep-dive
- Published: 2026-06-15

---

**SWC implements source map generation and debugging support through a centralized `SourceMap` architecture in `swc_common` that converts byte positions to line/column mappings, emits standard V3 source maps via `swc_sourcemap`, and exposes a proxy interface for WebAssembly plugins.**

The swc-project/swc compiler relies on a sophisticated source mapping system to maintain the connection between transformed JavaScript output and original source code. This architecture powers accurate error reporting, debugging support, and plugin interoperability while preserving the performance characteristics that distinguish SWC from other transpilers. At its core, the system maps byte positions to line and column coordinates using a global `SourceMap` container that integrates seamlessly with the compiler's AST spans.

## Core Architecture Components

### SourceMap and SourceFile

The `SourceMap` struct in [`crates/swc_common/src/source_map.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_common/src/source_map.rs) (lines 131-138) serves as the global container for all source files processed during a compilation session. It manages the mapping between raw byte positions and human-readable line/column coordinates.

Each input file is represented as a `SourceFile`, created via `SourceMap::new_source_file` (lines 219-227). This struct stores the file's raw bytes, its starting position in the global byte map, and pre-computed UTF-8/UTF-16 analysis data for accurate column calculations.

The map provides essential lookup methods like `lookup_char_pos` (lines 92-100) and `span_to_snippet` (lines 448-456), which convert `Span` values into actionable location data.

### SourceMapBuilder and V3 Format Output

To emit standard source maps, SWC uses `SourceMapBuilder` from the `swc_sourcemap` crate, located in [`crates/swc_sourcemap/src/builder.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_sourcemap/src/builder.rs). This component converts internal `(BytePos, LineCol)` pairs into the official V3 sourcemap JSON format.

The builder respects configuration options through the `SourceMapGenConfig` trait (implemented in [`crates/swc_ecma_transforms_testing/src/lib.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_testing/src/lib.rs) around line 1120), controlling whether to inline source content, transform file names, or omit column data.

### PluginSourceMapProxy for WebAssembly Plugins

When transforms run inside WebAssembly plugins, the host supplies a `PluginSourceMapProxy` defined in [`crates/swc_plugin_proxy/src/source_map/plugin_source_map_proxy.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_plugin_proxy/src/source_map/plugin_source_map_proxy.rs) (lines 6-30). This proxy forwards `lookup_char_pos` and `span_to_filename` calls to the host's `SourceMap`, making source data available to plugins without requiring the full `swc_common` crate in the plugin binary.

## The Source Map Generation Flow

1. **Registration**: During parsing, the compiler calls `SourceMap::new_source_file` to register each `SourceFile` and reserve a contiguous range of global byte positions.

2. **Span tracking**: AST nodes carry `Span` values represented as `(BytePos, BytePos)` tuples. When line/column information is needed, the compiler queries `source_map.lookup_char_pos(span.lo())`.

3. **Map construction**: After transformation, the compiler collects mappings via `source_map.build_source_map` (lines 1134-1195), which delegates to `swc_sourcemap::SourceMapBuilder` to produce a standard `swc_sourcemap::SourceMap` object.

4. **Plugin access**: Plugin code accesses the map through the generated proxy, which forwards calls to the host's implementation.

## Working with the SourceMap API

### Creating a SourceMap and Adding Files

```rust
use swc_common::{FileName, SourceMap, sync::Lrc};

let cm: Lrc<SourceMap> = Default::default();
let src = "const x = 10;".into();
let fm = cm.new_source_file(Lrc::new(FileName::Real("example.js".into())), src);

```

The `new_source_file` implementation resides in [`source_map.rs`](https://github.com/swc-project/swc/blob/main/source_map.rs) (lines 219-227), handling the UTF-8/UTF-16 analysis required for accurate column tracking.

### Converting Spans to Debug Locations

```rust
use swc_common::Span;

let span = fm.span;                     // whole file span
let loc = cm.lookup_char_pos(span.lo()); // ← `Loc { file, line, col, … }`
println!("{}:{}:{}", loc.file.name, loc.line, loc.col.to_usize() + 1);

```

The `lookup_char_pos` method (lines 91-100) returns a `Loc` struct containing the file name, line number, and column offset.

### Emitting Source Maps After Transformation

```rust
use swc_common::{SourceMap, BytePos, LineCol};
use swc_sourcemap::SourceMap as SwcSourceMap;

// Assume `cm` is the SourceMap used while parsing.
let mappings: Vec<(BytePos, LineCol)> = vec![
    // (generated byte position, original line/col)
    (BytePos(0), LineCol { line: 1, col: 0 }),
    // …
];
let config = swc_ecma_transforms_testing::SourceMapGenConfig::default();
let sourcemap: SwcSourceMap = cm.build_source_map(&mappings, None, config);
println!("{}", sourcemap.to_json_string());

```

The `build_source_map` method (lines 1134-1195) aggregates position data and delegates to `SourceMapBuilder` to generate the final JSON.

### Accessing Source Maps in Plugins

```rust
// Inside a plugin (generated by the macro crate)
use swc_core::plugin::proxies::PluginSourceMapProxy;

// The host supplies `source_map: PluginSourceMapProxy` in the plugin metadata.
// Example: retrieve the original filename for a span.
let filename = plugin_meta.source_map.span_to_filename(span);

```

The proxy in [`plugin_source_map_proxy.rs`](https://github.com/swc-project/swc/blob/main/plugin_source_map_proxy.rs) (lines 6-30) forwards requests to the host's `SourceMap` instance.

## Debugging Support and Diagnostic Integration

The `SourceMap` implements the `SourceMapper` trait (lines 3132-3168 in [`source_map.rs`](https://github.com/swc-project/swc/blob/main/source_map.rs)), allowing the diagnostics subsystem to convert `BytePos` values into human-readable locations without depending on the concrete map implementation.

For accurate error reporting in multi-byte UTF-8 contexts, the map uses `calc_utf16_offset` (lines 1195-1245) to compute column numbers that align with JavaScript debuggers and IDEs. Special handling for doctest offsets is available via `SourceMap::doctest_offset_line` (lines 80-88).

## Summary

- **SWC uses a global `SourceMap`** in `swc_common` to manage all source file metadata and byte-to-line mappings during compilation.
- **AST spans use `BytePos` tuples** that resolve to line/column coordinates via `lookup_char_pos` and related methods.
- **Standard V3 source maps** are generated through `SourceMapBuilder` in the `swc_sourcemap` crate, supporting configurable output via `SourceMapGenConfig`.
- **WebAssembly plugins** access source data through `PluginSourceMapProxy`, enabling debugging support without bloating plugin binaries.
- **Diagnostic accuracy** relies on UTF-8/UTF-16 analysis and the `SourceMapper` trait implementation for consistent error reporting.

## Frequently Asked Questions

### How does SWC maintain accurate column numbers for multi-byte characters?

SWC computes UTF-16 offsets using the `calc_utf16_offset` helper (lines 1195-1245 in [`source_map.rs`](https://github.com/swc-project/swc/blob/main/source_map.rs)) to ensure column numbers match JavaScript debugger expectations. Each `SourceFile` stores pre-computed UTF-8/UTF-16 analysis data when created via `new_source_file`, enabling accurate column calculations even for multibyte characters.

### Can plugins generate their own source maps, or must they use the host's map?

Plugins must access source map data through the `PluginSourceMapProxy` provided by the host. This proxy forwards calls like `lookup_char_pos` and `span_to_filename` to the host's `SourceMap`, ensuring consistency while keeping plugin binaries lightweight and avoiding the need to link the full `swc_common` crate.

### What source map format does SWC output?

SWC generates standard V3 source maps as defined by the Mozilla Source Map specification. The `SourceMapBuilder` in [`crates/swc_sourcemap/src/builder.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_sourcemap/src/builder.rs) handles the conversion from internal `LineCol` representations to the official JSON format, respecting options such as `inline_sources_content` and column omission.

### How does SWC support incremental compilation with source maps?

Each `SourceFile` receives a stable identifier (`StableSourceFileId`) when registered with the `SourceMap`. This allows the compiler to reuse existing source mappings and byte position allocations across incremental builds, maintaining consistent span references without regenerating the entire map.