#![allow(clippy::print_stdout, reason = "Examples are intended to print walkthrough output")]
use srcmap_generator::SourceMapGenerator;
use srcmap_remapping::{remap, remap_streaming};
use srcmap_sourcemap::{MappingsIter, SourceMap};
fn main() {
println!("=== Source Map Composition ===\n");
println!("--- Step 1: Inner map (tsc: original.ts -> intermediate.js) ---\n");
let mut inner_gen = SourceMapGenerator::new(Some("intermediate.js".to_string()));
let inner_src = inner_gen.add_source("original.ts");
inner_gen.set_source_content(
inner_src,
"function greet(name: string): string {\n const message = `Hello, ${name}!`;\n return message;\n}\n".to_string(),
);
let name_greet = inner_gen.add_name("greet");
let name_name = inner_gen.add_name("name");
let name_message = inner_gen.add_name("message");
inner_gen.add_mapping(0, 0, inner_src, 0, 0); inner_gen.add_named_mapping(0, 9, inner_src, 0, 9, name_greet); inner_gen.add_named_mapping(0, 15, inner_src, 0, 15, name_name);
inner_gen.add_mapping(1, 0, inner_src, 1, 0); inner_gen.add_named_mapping(1, 8, inner_src, 1, 10, name_message); inner_gen.add_mapping(1, 18, inner_src, 1, 20);
inner_gen.add_mapping(2, 0, inner_src, 2, 0); inner_gen.add_mapping(2, 4, inner_src, 2, 4); inner_gen.add_named_mapping(2, 11, inner_src, 2, 11, name_message);
inner_gen.add_mapping(3, 0, inner_src, 3, 0);
let inner_json = inner_gen.to_json();
let inner_map = SourceMap::from_json(&inner_json).unwrap();
println!(
" Inner map: {} mappings across {} lines",
inner_map.mapping_count(),
inner_map.line_count(),
);
println!(" Sources: {:?}", inner_map.sources);
println!(" Names: {:?}\n", inner_map.names);
println!("--- Step 2: Outer map (terser: intermediate.js -> output.min.js) ---\n");
let mut outer_gen = SourceMapGenerator::new(Some("output.min.js".to_string()));
let outer_src = outer_gen.add_source("intermediate.js");
let name_n = outer_gen.add_name("name");
let name_m = outer_gen.add_name("message");
outer_gen.add_mapping(0, 0, outer_src, 0, 0); outer_gen.add_mapping(0, 9, outer_src, 0, 9); outer_gen.add_named_mapping(0, 15, outer_src, 0, 15, name_n); outer_gen.add_mapping(0, 17, outer_src, 0, 21); outer_gen.add_mapping(0, 18, outer_src, 1, 4); outer_gen.add_named_mapping(0, 22, outer_src, 1, 8, name_m); outer_gen.add_mapping(0, 24, outer_src, 1, 18); outer_gen.add_mapping(0, 39, outer_src, 2, 4); outer_gen.add_named_mapping(0, 46, outer_src, 2, 11, name_m); outer_gen.add_mapping(0, 47, outer_src, 3, 0);
let outer_json = outer_gen.to_json();
let outer_map = SourceMap::from_json(&outer_json).unwrap();
println!(
" Outer map: {} mappings across {} lines",
outer_map.mapping_count(),
outer_map.line_count(),
);
println!(" Sources: {:?}", outer_map.sources);
println!(" Names: {:?}\n", outer_map.names);
println!("--- Step 3: Compose with remap() ---\n");
let composed = remap(&outer_map, |source| {
if source == "intermediate.js" {
Some(SourceMap::from_json(&inner_json).unwrap())
} else {
None
}
});
println!(
" Composed map: {} mappings across {} lines",
composed.mapping_count(),
composed.line_count(),
);
println!(" Sources: {:?}", composed.sources);
println!(" Names: {:?}", composed.names);
assert_eq!(composed.sources, vec!["original.ts"]);
assert!(
!composed.sources.contains(&"intermediate.js".to_string()),
"intermediate.js should be eliminated by composition",
);
println!("\n--- Step 4: Verify composed lookups ---\n");
let loc = composed.original_position_for(0, 0).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 0);
assert_eq!(loc.column, 0);
println!(" output.min.js 0:0 -> original.ts {}:{}", loc.line, loc.column);
let loc = composed.original_position_for(0, 9).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 0);
assert_eq!(loc.column, 9);
println!(" output.min.js 0:9 -> original.ts {}:{}", loc.line, loc.column);
let loc = composed.original_position_for(0, 15).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 0);
assert_eq!(loc.column, 15);
println!(
" output.min.js 0:15 -> original.ts {}:{} (name: {:?})",
loc.line,
loc.column,
loc.name.map(|n| composed.name(n)),
);
let loc = composed.original_position_for(0, 22).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 1);
assert_eq!(loc.column, 10);
println!(
" output.min.js 0:22 -> original.ts {}:{} (name: {:?})",
loc.line,
loc.column,
loc.name.map(|n| composed.name(n)),
);
let loc = composed.original_position_for(0, 39).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 2);
assert_eq!(loc.column, 4);
println!(" output.min.js 0:39 -> original.ts {}:{}", loc.line, loc.column);
let loc = composed.original_position_for(0, 47).unwrap();
assert_eq!(composed.source(loc.source), "original.ts");
assert_eq!(loc.line, 3);
assert_eq!(loc.column, 0);
println!(" output.min.js 0:47 -> original.ts {}:{}", loc.line, loc.column);
assert_eq!(composed.sources_content.len(), 1);
assert!(
composed.sources_content[0].is_some(),
"sourcesContent for original.ts should be preserved through composition",
);
println!("\n sourcesContent preserved: yes");
println!("\n--- Step 5: Compose with remap_streaming() ---\n");
let composed_streaming = remap_streaming(
MappingsIter::new(&outer_map.encode_mappings()),
&outer_map.sources,
&outer_map.names,
&outer_map.sources_content,
&outer_map.ignore_list,
outer_map.file.clone(),
|source| {
if source == "intermediate.js" {
Some(SourceMap::from_json(&inner_json).unwrap())
} else {
None
}
},
);
println!(
" Streaming composed map: {} mappings across {} lines",
composed_streaming.mapping_count(),
composed_streaming.line_count(),
);
println!(" Sources: {:?}", composed_streaming.sources);
assert_eq!(
composed_streaming.sources, composed.sources,
"streaming and non-streaming should produce the same sources",
);
let lookups: &[(u32, u32)] = &[(0, 0), (0, 9), (0, 15), (0, 22), (0, 39), (0, 47)];
for &(line, col) in lookups {
let a = composed.original_position_for(line, col).unwrap();
let b = composed_streaming.original_position_for(line, col).unwrap();
assert_eq!(composed.source(a.source), composed_streaming.source(b.source));
assert_eq!(a.line, b.line, "line mismatch for lookup ({line}, {col})");
assert_eq!(a.column, b.column, "column mismatch for lookup ({line}, {col})");
println!(
" ({line},{col}): remap -> {}:{} | streaming -> {}:{} (match)",
a.line, a.column, b.line, b.column,
);
}
println!("\nAll assertions passed.");
}