From 55ca15b73a802530948d9a8ea1505c0e1da775e8 Mon Sep 17 00:00:00 2001 From: DongYun Kang Date: Thu, 19 Mar 2026 11:53:25 +0900 Subject: [PATCH] feat(swc): add suppressSourceMapErrorLogging flag --- Cargo.lock | 1 + bindings/binding_core_wasm/src/types.rs | 7 ++ bindings/binding_minifier_wasm/src/types.rs | 7 ++ crates/swc/Cargo.toml | 1 + crates/swc/src/config/issue-9416.json | 3 + crates/swc/src/config/mod.rs | 7 ++ crates/swc/src/config/tests.rs | 11 ++ crates/swc/src/lib.rs | 18 ++- crates/swc/tests/source_map.rs | 115 +++++++++++++++++++- packages/types/index.ts | 7 ++ 10 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 crates/swc/src/config/issue-9416.json diff --git a/Cargo.lock b/Cargo.lock index 05d57fb612f6..94ed5259e27e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5609,6 +5609,7 @@ dependencies = [ "testing", "tokio", "tracing", + "tracing-subscriber", "url", "walkdir", ] diff --git a/bindings/binding_core_wasm/src/types.rs b/bindings/binding_core_wasm/src/types.rs index b8c286050967..8bc6e5d4fc47 100644 --- a/bindings/binding_core_wasm/src/types.rs +++ b/bindings/binding_core_wasm/src/types.rs @@ -593,6 +593,13 @@ export interface Config { */ sourceMaps?: boolean | "inline"; + /** + * Suppress logging for failures while loading input source maps. + * + * Defaults to `false`. + */ + suppressSourceMapErrorLogging?: boolean; + inlineSourcesContent?: boolean } diff --git a/bindings/binding_minifier_wasm/src/types.rs b/bindings/binding_minifier_wasm/src/types.rs index 409f26107a11..173aa8bfd9cb 100644 --- a/bindings/binding_minifier_wasm/src/types.rs +++ b/bindings/binding_minifier_wasm/src/types.rs @@ -593,6 +593,13 @@ export interface Config { */ sourceMaps?: boolean | "inline"; + /** + * Suppress logging for failures while loading input source maps. + * + * Defaults to `false`. + */ + suppressSourceMapErrorLogging?: boolean; + inlineSourcesContent?: boolean } diff --git a/crates/swc/Cargo.toml b/crates/swc/Cargo.toml index 8682592c5d97..77d86f04e06d 100644 --- a/crates/swc/Cargo.toml +++ b/crates/swc/Cargo.toml @@ -167,6 +167,7 @@ swc_ecma_transforms_compat = { version = "44.0.0", path = "../swc_ecma_transform ] } swc_malloc = { version = "1.2.5", path = "../swc_malloc" } testing = { version = "20.0.0", path = "../testing" } +tracing-subscriber = { workspace = true } [[example]] name = "transform" diff --git a/crates/swc/src/config/issue-9416.json b/crates/swc/src/config/issue-9416.json new file mode 100644 index 000000000000..6346f74ac017 --- /dev/null +++ b/crates/swc/src/config/issue-9416.json @@ -0,0 +1,3 @@ +{ + "suppressSourceMapErrorLogging": true +} diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs index bdd9ad55bb88..6f709c20c1f8 100644 --- a/crates/swc/src/config/mod.rs +++ b/crates/swc/src/config/mod.rs @@ -964,6 +964,7 @@ impl Options { source_maps: source_maps.unwrap_or(SourceMapsConfig::Bool(false)), inline_sources_content: cfg.inline_sources_content.into_bool(), input_source_map: cfg.input_source_map.clone().unwrap_or_default(), + suppress_source_map_error_logging: cfg.suppress_source_map_error_logging.into_bool(), output_path: output_path.map(|v| v.to_path_buf()), source_root, source_file_name, @@ -1160,6 +1161,10 @@ pub struct Config { #[serde(default)] pub input_source_map: Option, + /// Suppress logging for failures while loading input source maps. + #[serde(default)] + pub suppress_source_map_error_logging: BoolConfig, + /// Possible values are: `'inline'`, `true`, `false`. #[serde(default)] pub source_maps: Option, @@ -1284,6 +1289,7 @@ pub struct BuiltInput { pub external_helpers: bool, pub source_maps: SourceMapsConfig, pub input_source_map: InputSourceMap, + pub suppress_source_map_error_logging: bool, pub is_module: IsModule, pub output_path: Option, @@ -1325,6 +1331,7 @@ where external_helpers: self.external_helpers, source_maps: self.source_maps, input_source_map: self.input_source_map, + suppress_source_map_error_logging: self.suppress_source_map_error_logging, is_module: self.is_module, output_path: self.output_path, source_root: self.source_root, diff --git a/crates/swc/src/config/tests.rs b/crates/swc/src/config/tests.rs index cb7eae53af1f..021a78839b0a 100644 --- a/crates/swc/src/config/tests.rs +++ b/crates/swc/src/config/tests.rs @@ -30,3 +30,14 @@ fn issue_6996() { let rc = parse_swcrc(include_str!("issue-6996.json")).expect("failed to parse"); dbg!(&rc); } + +#[test] +fn issue_9416() { + let rc = parse_swcrc(include_str!("issue-9416.json")).expect("failed to parse"); + let config = rc + .into_config(None) + .expect("failed to convert swcrc to config") + .expect("config should be available"); + + assert!(config.suppress_source_map_error_logging.into_bool()); +} diff --git a/crates/swc/src/lib.rs b/crates/swc/src/lib.rs index 5123daad11b1..98499dd166d4 100644 --- a/crates/swc/src/lib.rs +++ b/crates/swc/src/lib.rs @@ -250,6 +250,7 @@ impl Compiler { &self, fm: &SourceFile, input_src_map: &InputSourceMap, + suppress_source_map_error_logging: bool, comments: &[Comment], is_default: bool, ) -> Result, Error> { @@ -345,11 +346,13 @@ impl Compiler { .as_ref() .is_err_and(|err| err.kind() == ErrorKind::NotFound) { - warn!( - "source map is specified by sourceMappingURL but \ - there's no source map at `{}`", - path - ); + if !suppress_source_map_error_logging { + warn!( + "source map is specified by sourceMappingURL but \ + there's no source map at `{}`", + path + ); + } return Ok(None); } @@ -397,7 +400,9 @@ impl Compiler { match result { Ok(r) => r, Err(err) => { - tracing::error!("failed to read input source map: {:?}", err); + if !suppress_source_map_error_logging { + tracing::error!("failed to read input source map: {:?}", err); + } None } } @@ -731,6 +736,7 @@ impl Compiler { self.get_orig_src_map( &fm, &config.input_source_map, + config.suppress_source_map_error_logging, config .comments .get_trailing(config.program.span_hi()) diff --git a/crates/swc/tests/source_map.rs b/crates/swc/tests/source_map.rs index 602f9e212b3b..fe008060ee95 100644 --- a/crates/swc/tests/source_map.rs +++ b/crates/swc/tests/source_map.rs @@ -6,7 +6,7 @@ use std::{ io::Write, path::{Path, PathBuf}, process::{Command, Output}, - sync::Arc, + sync::{Arc, Mutex}, }; use anyhow::{Context, Error}; @@ -15,10 +15,11 @@ use swc::{ Config, InputSourceMap, IsModule, JscConfig, JscExperimental, ModuleConfig, Options, SourceMapsConfig, }, - Compiler, + try_with_handler, Compiler, HandlerOpts, }; use swc_ecma_parser::Syntax; use testing::{assert_eq, NormalizedOutput, StdErr, Tester}; +use tracing_subscriber::fmt::MakeWriter; use walkdir::WalkDir; fn file(f: &str, config: Config) -> Result<(), StdErr> { @@ -613,3 +614,113 @@ export const fixupRiskConfigData = (data: any): types.RiskConfigType => { Ok(()) }); } + +#[derive(Clone, Default)] +struct LogBuffer { + inner: Arc>>, +} + +struct LogBufferWriter { + inner: Arc>>, +} + +impl<'a> MakeWriter<'a> for LogBuffer { + type Writer = LogBufferWriter; + + fn make_writer(&'a self) -> Self::Writer { + LogBufferWriter { + inner: Arc::clone(&self.inner), + } + } +} + +impl Write for LogBufferWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner + .lock() + .expect("log buffer mutex poisoned") + .extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +fn collect_source_map_logs(suppress_source_map_error_logging: bool) -> String { + let log_buffer = LogBuffer::default(); + let subscriber = tracing_subscriber::fmt() + .with_ansi(false) + .without_time() + .with_target(false) + .with_max_level(tracing::Level::WARN) + .with_writer(log_buffer.clone()) + .finish(); + + tracing::subscriber::with_default(subscriber, || { + let cm = Arc::new(swc_common::SourceMap::new( + swc_common::FilePathMapping::empty(), + )); + let c = Compiler::new(cm.clone()); + let filename = temp_dir().join("swc-issue-9416-missing-input-sourcemap.js"); + let fm = cm.new_source_file( + swc_common::FileName::Real(filename).into(), + "console.log('x');\n//# sourceMappingURL=missing.js.map".to_string(), + ); + + swc_common::GLOBALS.set(&Default::default(), || { + try_with_handler(cm.clone(), HandlerOpts::default(), |handler| { + let _ = c + .process_js_file( + fm, + handler, + &Options { + swcrc: false, + source_maps: Some(SourceMapsConfig::Bool(true)), + config: Config { + input_source_map: Some(InputSourceMap::Bool(true)), + suppress_source_map_error_logging: + suppress_source_map_error_logging.into(), + ..Default::default() + }, + ..Default::default() + }, + ) + .expect("failed to process fixture"); + + Ok(()) + }) + .expect("failed to run transform with handler"); + }); + }); + + let bytes = log_buffer + .inner + .lock() + .expect("log buffer mutex poisoned") + .clone(); + String::from_utf8(bytes).expect("captured logs should be utf-8") +} + +#[test] +fn issue_9416_suppress_source_map_error_logging() { + const MISSING_MAP_WARN: &str = + "source map is specified by sourceMappingURL but there's no source map at"; + const SOURCE_MAP_ERROR: &str = "failed to read input source map"; + + let logs_with_default = collect_source_map_logs(false); + assert!( + logs_with_default.contains(MISSING_MAP_WARN) + || logs_with_default.contains(SOURCE_MAP_ERROR), + "expected source map loading errors to be logged by default, got: {logs_with_default}" + ); + + let logs_with_suppression = collect_source_map_logs(true); + assert!( + !logs_with_suppression.contains(MISSING_MAP_WARN) + && !logs_with_suppression.contains(SOURCE_MAP_ERROR), + "source map loading errors should be suppressed when suppressSourceMapErrorLogging=true, \ + got: {logs_with_suppression}" + ); +} diff --git a/packages/types/index.ts b/packages/types/index.ts index ec424ef7207b..2e19c27c3d6e 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -566,6 +566,13 @@ export interface Config { */ sourceMaps?: boolean | "inline"; + /** + * Suppress logging for failures while loading input source maps. + * + * Defaults to `false`. + */ + suppressSourceMapErrorLogging?: boolean; + inlineSourcesContent?: boolean; }