jstime_core 0.66.0

Another JS Runtime
Documentation
//! Source map support for better error reporting.
//!
//! This module provides basic infrastructure for source map support.
//! Source maps allow mapping errors in transpiled/bundled code back to
//! the original source for better debugging.
//!
//! ## Future Implementation
//!
//! To fully implement source maps, we would need to:
//! 1. Add a dependency on a source map library (e.g., `sourcemap` crate)
//! 2. Parse source map files (inline or external)
//! 3. Map V8 error locations to original source locations
//! 4. Cache parsed source maps for performance
//! 5. Handle different source map versions and formats
//!
//! ## Source Map Format
//!
//! Source maps typically have this structure:
//! ```json
//! {
//!   "version": 3,
//!   "file": "out.js",
//!   "sourceRoot": "",
//!   "sources": ["foo.js", "bar.js"],
//!   "sourcesContent": ["...", "..."],
//!   "names": ["src", "maps", "are", "fun"],
//!   "mappings": "A,AAAB;;ABCDE;"
//! }
//! ```
//!
//! ## Integration Points
//!
//! Source maps would integrate with error formatting in these ways:
//! - When loading a module, check for source maps (inline or external)
//! - Store source map data in the module map
//! - In `format_exception`, look up source maps for the error location
//! - Transform the file path and line/column numbers to original source
//! - Display the original source code instead of transpiled code

/// Placeholder for source map data.
///
/// In a full implementation, this would contain:
/// - Parsed source map mappings
/// - Original source content
/// - Source file names
#[allow(dead_code)]
pub(crate) struct SourceMap {
    // Future: Add source map data structures here
}

impl SourceMap {
    /// Create a new empty source map.
    #[allow(dead_code)]
    pub(crate) fn new() -> Self {
        Self {}
    }

    /// Parse a source map from JSON content.
    ///
    /// # Arguments
    ///
    /// * `json` - The source map JSON string
    ///
    /// # Returns
    ///
    /// A Result containing the parsed source map or an error message.
    #[allow(dead_code)]
    pub(crate) fn parse(_json: &str) -> Result<Self, String> {
        // Future: Implement source map parsing
        Err("Source map parsing not yet implemented".to_string())
    }

    /// Map a location in generated code to the original source.
    ///
    /// # Arguments
    ///
    /// * `line` - Line number in generated code (0-based)
    /// * `column` - Column number in generated code (0-based)
    ///
    /// # Returns
    ///
    /// A tuple of (source_file, original_line, original_column) if found.
    #[allow(dead_code)]
    pub(crate) fn map_location(&self, _line: u32, _column: u32) -> Option<(String, u32, u32)> {
        // Future: Implement location mapping
        None
    }

    /// Get the original source content for a source file.
    ///
    /// # Arguments
    ///
    /// * `source_file` - The source file name
    ///
    /// # Returns
    ///
    /// The original source content if available.
    #[allow(dead_code)]
    pub(crate) fn get_source_content(&self, _source_file: &str) -> Option<&str> {
        // Future: Implement source content retrieval
        None
    }
}

/// Detect if a JavaScript file has an inline source map.
///
/// Inline source maps are typically at the end of the file:
/// `//# sourceMappingURL=data:application/json;base64,...`
///
/// # Arguments
///
/// * `content` - The JavaScript file content
///
/// # Returns
///
/// The base64-encoded source map content if found.
#[allow(dead_code)]
pub(crate) fn detect_inline_sourcemap(content: &str) -> Option<&str> {
    // Look for inline source map comment
    let prefix = "//# sourceMappingURL=data:application/json;base64,";

    for line in content.lines().rev().take(10) {
        let trimmed = line.trim();
        if let Some(data) = trimmed.strip_prefix(prefix) {
            return Some(data);
        }
    }

    None
}

/// Detect if a JavaScript file references an external source map.
///
/// External source map references look like:
/// `//# sourceMappingURL=bundle.js.map`
///
/// # Arguments
///
/// * `content` - The JavaScript file content
///
/// # Returns
///
/// The source map file name if found.
#[allow(dead_code)]
pub(crate) fn detect_external_sourcemap(content: &str) -> Option<&str> {
    // Look for external source map reference
    let prefix = "//# sourceMappingURL=";

    for line in content.lines().rev().take(10) {
        let trimmed = line.trim();
        if let Some(url) = trimmed.strip_prefix(prefix) {
            // Skip data URLs (those are inline)
            if !url.starts_with("data:") {
                return Some(url.trim());
            }
        }
    }

    None
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect_inline_sourcemap() {
        let js = r#"
console.log("test");
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozfQ==
        "#;

        let result = detect_inline_sourcemap(js);
        assert_eq!(result, Some("eyJ2ZXJzaW9uIjozfQ=="));
    }

    #[test]
    fn test_detect_external_sourcemap() {
        let js = r#"
console.log("test");
//# sourceMappingURL=bundle.js.map
        "#;

        let result = detect_external_sourcemap(js);
        assert_eq!(result, Some("bundle.js.map"));
    }

    #[test]
    fn test_no_sourcemap() {
        let js = r#"
console.log("test");
        "#;

        assert_eq!(detect_inline_sourcemap(js), None);
        assert_eq!(detect_external_sourcemap(js), None);
    }
}