diff --git a/crates/xtask/src/build_wasm.rs b/crates/xtask/src/build_wasm.rs index a46be45d..4ad35e4b 100644 --- a/crates/xtask/src/build_wasm.rs +++ b/crates/xtask/src/build_wasm.rs @@ -3,7 +3,7 @@ use std::{ ffi::{OsStr, OsString}, fmt::Write, fs, - path::PathBuf, + path::{Path, PathBuf}, process::Command, time::Duration, }; @@ -16,7 +16,9 @@ use notify::{ }; use notify_debouncer_full::new_debouncer; -use crate::{bail_on_err, watch_wasm, BuildWasm, EMSCRIPTEN_TAG}; +use crate::{ + bail_on_err, embed_sources::embed_sources_in_map, watch_wasm, BuildWasm, EMSCRIPTEN_TAG, +}; #[derive(PartialEq, Eq)] enum EmccSource { @@ -48,10 +50,14 @@ const EXPORTED_RUNTIME_METHODS: [&str; 19] = [ ]; pub fn run_wasm(args: &BuildWasm) -> Result<()> { - let mut emscripten_flags = vec!["-O3", "--minify", "0"]; + let mut emscripten_flags = if args.debug { + vec!["-O0", "--minify", "0"] + } else { + vec!["-O3", "--minify", "0"] + }; if args.debug { - emscripten_flags.extend(["-s", "ASSERTIONS=1", "-s", "SAFE_HEAP=1", "-O0", "-g"]); + emscripten_flags.extend(["-s", "ASSERTIONS=1", "-s", "SAFE_HEAP=1", "-g"]); } if args.verbose { @@ -287,6 +293,17 @@ fn build_wasm(cmd: &mut Command, edit_tsd: bool) -> Result<()> { fs::write(file, content)?; } + // Post-process the source map to embed source content for optimized builds + let map_path = Path::new("lib") + .join("binding_web") + .join("lib") + .join("web-tree-sitter.wasm.map"); + if map_path.exists() { + if let Err(e) = embed_sources_in_map(&map_path) { + eprintln!("Warning: Failed to embed sources in source map: {e}"); + } + } + Ok(()) } diff --git a/crates/xtask/src/embed_sources.rs b/crates/xtask/src/embed_sources.rs new file mode 100644 index 00000000..ce8ec403 --- /dev/null +++ b/crates/xtask/src/embed_sources.rs @@ -0,0 +1,61 @@ +use anyhow::Result; +use std::fs; +use std::path::Path; + +/// Restores sourcesContent if it was stripped by Binaryen. +/// +/// This is a workaround for Binaryen where `wasm-opt -O2` and higher +/// optimization levels strip the `sourcesContent` field from source maps, +/// even when the source map was generated with `--sources` flag. +/// +/// This is fixed upstream in Binaryen as of Apr 9, 2025, but there hasn't been a release with the fix yet. +/// See: +/// +/// This reads the original source files and embeds them in the +/// source map's `sourcesContent` field, making debugging possible even +/// with optimized builds. +/// +/// TODO: Once Binaryen releases a version with the fix, and emscripten updates to that +/// version, and we update our emscripten version, this function can be removed. +pub fn embed_sources_in_map(map_path: &Path) -> Result<()> { + let map_content = fs::read_to_string(map_path)?; + let mut map: serde_json::Value = serde_json::from_str(&map_content)?; + + if let Some(sources_content) = map.get("sourcesContent") { + if let Some(arr) = sources_content.as_array() { + if !arr.is_empty() && arr.iter().any(|v| !v.is_null()) { + return Ok(()); + } + } + } + + let sources = map["sources"] + .as_array() + .ok_or_else(|| anyhow::anyhow!("No sources array in source map"))?; + + let map_dir = map_path.parent().unwrap_or(Path::new(".")); + let mut sources_content = Vec::new(); + + for source in sources { + let source_path = source.as_str().unwrap_or(""); + let full_path = map_dir.join(source_path); + + let content = if full_path.exists() { + match fs::read_to_string(&full_path) { + Ok(content) => serde_json::Value::String(content), + Err(_) => serde_json::Value::Null, + } + } else { + serde_json::Value::Null + }; + + sources_content.push(content); + } + + map["sourcesContent"] = serde_json::Value::Array(sources_content); + + let output = serde_json::to_string(&map)?; + fs::write(map_path, output)?; + + Ok(()) +} diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 600ae83e..1c59f4f4 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -3,6 +3,7 @@ mod build_wasm; mod bump; mod check_wasm_exports; mod clippy; +mod embed_sources; mod fetch; mod generate; mod test;