veryl_sourcemap/
sourcemap.rs

1use crate::SourceMapError;
2use relative_path::PathExt;
3use sourcemap::SourceMapBuilder;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7const LINK_HEADER: &str = "//# sourceMappingURL=";
8
9pub struct SourceMap {
10    pub src_path: PathBuf,
11    pub dst_path: PathBuf,
12    pub map_path: PathBuf,
13    pub src_path_from_map: String,
14    pub map_path_from_dst: String,
15    builder: SourceMapBuilder,
16    source_map: Option<sourcemap::SourceMap>,
17}
18
19impl SourceMap {
20    pub fn new(src_path: &Path, dst_path: &Path, map_path: &Path) -> Self {
21        let src_path = src_path.to_path_buf();
22        let dst_path = dst_path.to_path_buf();
23        let map_path = map_path.to_path_buf();
24        let src_path_from_map = if let Ok(x) = src_path.relative_to(map_path.parent().unwrap()) {
25            x.as_str().to_owned()
26        } else {
27            src_path.to_string_lossy().to_string()
28        };
29        let map_path_from_dst = if let Ok(x) = map_path.relative_to(dst_path.parent().unwrap()) {
30            x.as_str().to_owned()
31        } else {
32            map_path.to_string_lossy().to_string()
33        };
34        let builder = SourceMapBuilder::new(Some(&map_path.file_name().unwrap().to_string_lossy()));
35        Self {
36            src_path,
37            dst_path,
38            map_path,
39            src_path_from_map,
40            map_path_from_dst,
41            builder,
42            source_map: None,
43        }
44    }
45
46    pub fn from_src(src_path: &Path) -> Result<Self, SourceMapError> {
47        let src = fs::read_to_string(src_path).map_err(|x| SourceMapError::io(x, src_path))?;
48
49        if let Some(line) = src.lines().last()
50            && line.starts_with(LINK_HEADER)
51        {
52            let map_path = line.strip_prefix(LINK_HEADER).unwrap();
53            let map_path = src_path.parent().unwrap().join(map_path);
54            let text = fs::read(&map_path).map_err(|x| SourceMapError::io(x, &map_path))?;
55
56            let src_path = src_path.to_path_buf();
57            let dst_path = PathBuf::new();
58            let src_path_from_map = String::new();
59            let map_path_from_dst = String::new();
60            let builder =
61                SourceMapBuilder::new(Some(&map_path.file_name().unwrap().to_string_lossy()));
62            let source_map = Some(sourcemap::SourceMap::from_reader(text.as_slice())?);
63
64            return Ok(Self {
65                src_path,
66                dst_path,
67                map_path,
68                src_path_from_map,
69                map_path_from_dst,
70                builder,
71                source_map,
72            });
73        }
74
75        Err(SourceMapError::NotFound)
76    }
77
78    pub fn add(
79        &mut self,
80        dst_line: u32,
81        dst_column: u32,
82        src_line: u32,
83        src_column: u32,
84        name: &str,
85    ) {
86        // Line and column of sourcemap crate is 0-based
87        let dst_line = dst_line - 1;
88        let dst_column = dst_column - 1;
89        let src_line = src_line - 1;
90        let src_column = src_column - 1;
91
92        self.builder.add(
93            dst_line,
94            dst_column,
95            src_line,
96            src_column,
97            Some(&self.src_path_from_map),
98            Some(name),
99            false,
100        );
101    }
102
103    pub fn set_source_content(&mut self, content: &str) {
104        let id = self.builder.add_source(&self.src_path_from_map);
105        self.builder.set_source_contents(id, Some(content));
106    }
107
108    pub fn build(&mut self) {
109        let mut builder =
110            SourceMapBuilder::new(Some(&self.map_path.file_name().unwrap().to_string_lossy()));
111        std::mem::swap(&mut builder, &mut self.builder);
112        self.source_map = Some(builder.into_sourcemap());
113    }
114
115    pub fn get_link(&self) -> String {
116        format!("{}{}", LINK_HEADER, self.map_path_from_dst)
117    }
118
119    pub fn to_bytes(&self) -> Result<Vec<u8>, SourceMapError> {
120        if let Some(ref x) = self.source_map {
121            let mut ret = Vec::new();
122            x.to_writer(&mut ret)?;
123            Ok(ret)
124        } else {
125            Err(SourceMapError::NotFound)
126        }
127    }
128
129    pub fn lookup(&self, line: u32, column: u32) -> Option<(PathBuf, u32, u32)> {
130        if let Some(ref x) = self.source_map {
131            if let Some(token) = x.lookup_token(line - 1, column - 1) {
132                if let Some(path) = token.get_source() {
133                    let path = self.map_path.parent().unwrap().join(path);
134                    if let Ok(path) = fs::canonicalize(path) {
135                        let line = token.get_src_line() + 1;
136                        let column = token.get_src_col() + 1;
137                        Some((path, line, column))
138                    } else {
139                        None
140                    }
141                } else {
142                    None
143                }
144            } else {
145                None
146            }
147        } else {
148            None
149        }
150    }
151}