Skip to content

Commit

Permalink
feat: allow specifying a base for source maps (#262)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret authored Jul 25, 2024
1 parent 91c3b76 commit 0aa7f51
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 5 deletions.
35 changes: 30 additions & 5 deletions src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::swc::ast::Program;
use crate::swc::codegen::text_writer::JsWriter;
use crate::swc::codegen::Node;
use crate::swc::common::FileName;
use crate::ModuleSpecifier;
use crate::SourceMap;

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -27,6 +28,11 @@ pub enum SourceMapOption {
pub struct EmitOptions {
/// How and if source maps should be generated.
pub source_map: SourceMapOption,
/// Base url to use for source maps.
///
/// When a base is provided, when mapping source names in the source map, the
/// name will be relative to the base.
pub source_map_base: Option<ModuleSpecifier>,
/// The `"file"` field of the generated source map.
pub source_map_file: Option<String>,
/// Whether to inline the source contents in the source map. Defaults to `true`.
Expand All @@ -39,6 +45,7 @@ impl Default for EmitOptions {
fn default() -> Self {
EmitOptions {
source_map: SourceMapOption::default(),
source_map_base: None,
source_map_file: None,
inline_sources: true,
remove_comments: false,
Expand Down Expand Up @@ -122,6 +129,7 @@ pub fn emit(
let mut map_buf = Vec::new();
let source_map_config = SourceMapConfig {
inline_sources: emit_options.inline_sources,
maybe_base: emit_options.source_map_base.as_ref(),
};
let mut source_map = source_map.build_source_map_with_config(
&src_map_buf,
Expand Down Expand Up @@ -169,13 +177,29 @@ pub fn emit(
/// Implements a configuration trait for source maps that reflects the logic
/// to embed sources in the source map or not.
#[derive(Debug)]
pub struct SourceMapConfig {
pub struct SourceMapConfig<'a> {
pub inline_sources: bool,
pub maybe_base: Option<&'a ModuleSpecifier>,
}

impl crate::swc::common::source_map::SourceMapGenConfig for SourceMapConfig {
impl<'a> crate::swc::common::source_map::SourceMapGenConfig
for SourceMapConfig<'a>
{
fn file_name_to_source(&self, f: &FileName) -> String {
f.to_string()
match f {
FileName::Url(specifier) => self
.maybe_base
.and_then(|base| {
debug_assert!(
base.as_str().ends_with('/'),
"source map base should end with a slash"
);
base.make_relative(specifier)
})
.filter(|relative| !relative.is_empty())
.unwrap_or_else(|| f.to_string()),
_ => f.to_string(),
}
}

fn inline_sources_content(&self, f: &FileName) -> bool {
Expand All @@ -193,10 +217,11 @@ pub fn swc_codegen_config() -> crate::swc::codegen::Config {
// inspect the struct on swc upgrade and explicitly specify any
// new options here in order to ensure we maintain these settings.
let mut config = crate::swc::codegen::Config::default();
config.minify = false;
config.target = crate::ES_VERSION;
config.ascii_only = false;
config.minify = false;
config.omit_last_semi = false;
config.target = crate::ES_VERSION;
config.emit_assert_for_import_attributes = false;
config.inline_script = false;
config
}
85 changes: 85 additions & 0 deletions src/transpiling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,91 @@ const a = _jsx(Foo, {
);
}

#[test]
fn test_source_map_base() {
#[track_caller]
fn run_test(file_name: &str, base: &str, expected: &str) {
let specifier = ModuleSpecifier::parse(file_name).unwrap();
let source = r#"{ const foo = "bar"; };"#;
let module = parse_module(ParseParams {
specifier,
text: source.into(),
media_type: MediaType::Tsx,
capture_tokens: false,
maybe_syntax: None,
scope_analysis: false,
})
.unwrap();
let emit_options = EmitOptions {
source_map: SourceMapOption::Separate,
source_map_base: Some(ModuleSpecifier::parse(base).unwrap()),
..Default::default()
};
let emit_result = module
.transpile(&TranspileOptions::default(), &emit_options)
.unwrap()
.into_source()
.into_string()
.unwrap();
assert_eq!(
&emit_result.text,
r#"{
const foo = "bar";
}"#
);
let value: serde_json::Value =
serde_json::from_str(&emit_result.source_map.unwrap()).unwrap();
assert_eq!(
value,
serde_json::json!({
"version":3,
"sources":[expected],
"sourcesContent":["{ const foo = \"bar\"; };"],
"names":[],
"mappings":"AAAA;EAAE,MAAM,MAAM;AAAO"
})
);
}

run_test(
"https://deno.land/x/mod.tsx",
"https://deno.land/x/",
"mod.tsx",
);
run_test(
"https://deno.land/x/mod.tsx",
"https://deno.land/",
"x/mod.tsx",
);
run_test(
"https://deno.land/x/mod.tsx",
"file:///home/user/",
"https://deno.land/x/mod.tsx",
);
run_test(
"https://deno.land/x/mod.tsx",
"https://example.com/",
"https://deno.land/x/mod.tsx",
);
run_test(
"https://example.com/base/",
"https://example.com/base/",
// maybe not ideal, but this is ok
"https://example.com/base/",
);
run_test("file:///example.ts", "file:///", "example.ts");
run_test(
"file:///sub_dir/example.ts",
"file:///",
"sub_dir/example.ts",
);
run_test(
"file:///sub_dir/example.ts",
"file:///sub_dir/",
"example.ts",
);
}

#[test]
fn test_source_map_with_file() {
let specifier =
Expand Down

0 comments on commit 0aa7f51

Please sign in to comment.