1use std::{borrow::Cow, io::Write, ops::Deref, sync::Arc};
2
3use anyhow::Result;
4use bytes_str::BytesStr;
5use once_cell::sync::Lazy;
6use ref_cast::RefCast;
7use regex::Regex;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use swc_sourcemap::{DecodedMap, SourceMap as RegularMap, SourceMapBuilder, SourceMapIndex};
10use turbo_rcstr::RcStr;
11use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
12use turbo_tasks_fs::{
13 File, FileContent, FileSystem, FileSystemPath, VirtualFileSystem,
14 rope::{Rope, RopeBuilder},
15};
16
17use crate::{
18 SOURCE_URL_PROTOCOL, asset::AssetContent, source::Source,
19 source_map::utils::add_default_ignore_list, source_pos::SourcePos,
20 virtual_source::VirtualSource,
21};
22
23pub(crate) mod source_map_asset;
24pub mod utils;
25
26pub use source_map_asset::SourceMapAsset;
27
28static SOURCEMAP_CRATE_NONE_U32: u32 = !0;
30
31#[turbo_tasks::value(transparent)]
32pub struct OptionStringifiedSourceMap(Option<Rope>);
33
34#[turbo_tasks::value_impl]
35impl OptionStringifiedSourceMap {
36 #[turbo_tasks::function]
37 pub fn none() -> Vc<Self> {
38 Vc::cell(None)
39 }
40}
41
42#[turbo_tasks::value_trait]
44pub trait GenerateSourceMap {
45 #[turbo_tasks::function]
47 fn generate_source_map(self: Vc<Self>) -> Vc<OptionStringifiedSourceMap>;
48
49 #[turbo_tasks::function]
51 fn by_section(self: Vc<Self>, _section: RcStr) -> Vc<OptionStringifiedSourceMap> {
52 Vc::cell(None)
53 }
54}
55
56#[turbo_tasks::value(shared, cell = "new")]
67#[derive(Debug)]
68pub enum SourceMap {
69 Decoded(#[turbo_tasks(trace_ignore)] InnerSourceMap),
71 Sectioned(#[turbo_tasks(trace_ignore)] SectionedSourceMap),
74}
75
76#[turbo_tasks::value(transparent)]
77pub struct OptionSourceMap(Option<SourceMap>);
78
79#[turbo_tasks::value_impl]
80impl OptionSourceMap {
81 #[turbo_tasks::function]
82 pub fn none() -> Vc<Self> {
83 Vc::cell(None)
84 }
85}
86
87impl OptionSourceMap {
88 pub fn none_resolved() -> ResolvedVc<Self> {
89 ResolvedVc::cell(None)
90 }
91}
92#[turbo_tasks::value(transparent)]
93#[derive(Clone, Debug)]
94pub struct Tokens(Vec<Token>);
95
96#[turbo_tasks::value]
102#[derive(Clone, Debug)]
103pub enum Token {
104 Synthetic(SyntheticToken),
105 Original(OriginalToken),
106}
107
108#[turbo_tasks::value]
109#[derive(Clone, Debug)]
110pub struct TokenWithSource {
111 pub token: Token,
112 pub source_content: Option<ResolvedVc<Box<dyn Source>>>,
113}
114
115#[turbo_tasks::value]
118#[derive(Clone, Debug)]
119pub struct SyntheticToken {
120 pub generated_line: u32,
121 pub generated_column: u32,
122 pub guessed_original_file: Option<RcStr>,
123}
124
125#[turbo_tasks::value]
128#[derive(Clone, Debug)]
129pub struct OriginalToken {
130 pub generated_line: u32,
131 pub generated_column: u32,
132 pub original_file: RcStr,
133 pub original_line: u32,
134 pub original_column: u32,
135 pub name: Option<RcStr>,
136}
137
138impl Token {
139 pub fn generated_line(&self) -> u32 {
140 match self {
141 Self::Original(t) => t.generated_line,
142 Self::Synthetic(t) => t.generated_line,
143 }
144 }
145
146 pub fn generated_column(&self) -> u32 {
147 match self {
148 Self::Original(t) => t.generated_column,
149 Self::Synthetic(t) => t.generated_column,
150 }
151 }
152}
153
154impl From<swc_sourcemap::Token<'_>> for Token {
155 fn from(t: swc_sourcemap::Token) -> Self {
156 if t.has_source() {
157 Token::Original(OriginalToken {
158 generated_line: t.get_dst_line(),
159 generated_column: t.get_dst_col(),
160 original_file: RcStr::from(
161 t.get_source()
162 .expect("already checked token has source")
163 .clone(),
164 ),
165 original_line: t.get_src_line(),
166 original_column: t.get_src_col(),
167 name: t.get_name().cloned().map(RcStr::from),
168 })
169 } else {
170 Token::Synthetic(SyntheticToken {
171 generated_line: t.get_dst_line(),
172 generated_column: t.get_dst_col(),
173 guessed_original_file: None,
174 })
175 }
176 }
177}
178
179impl TryInto<swc_sourcemap::RawToken> for Token {
180 type Error = std::num::ParseIntError;
181
182 fn try_into(self) -> Result<swc_sourcemap::RawToken, Self::Error> {
183 Ok(match self {
184 Self::Original(t) => swc_sourcemap::RawToken {
185 dst_col: t.generated_column,
186 dst_line: t.generated_line,
187 name_id: match t.name {
188 None => SOURCEMAP_CRATE_NONE_U32,
189 Some(name) => name.parse()?,
190 },
191 src_col: t.original_column,
192 src_line: t.original_line,
193 src_id: t.original_file.parse()?,
194 is_range: false,
195 },
196 Self::Synthetic(t) => swc_sourcemap::RawToken {
197 dst_col: t.generated_column,
198 dst_line: t.generated_line,
199 name_id: SOURCEMAP_CRATE_NONE_U32,
200 src_col: SOURCEMAP_CRATE_NONE_U32,
201 src_line: SOURCEMAP_CRATE_NONE_U32,
202 src_id: SOURCEMAP_CRATE_NONE_U32,
203 is_range: false,
204 },
205 })
206 }
207}
208
209impl SourceMap {
210 pub fn empty_uncelled() -> Self {
211 let mut builder = SourceMapBuilder::new(None);
212 builder.add(0, 0, 0, 0, None, None, false);
213 SourceMap::new_regular(builder.into_sourcemap())
214 }
215
216 pub fn new_regular(map: RegularMap) -> Self {
218 Self::new_decoded(DecodedMap::Regular(map))
219 }
220
221 pub fn new_decoded(map: DecodedMap) -> Self {
223 SourceMap::Decoded(InnerSourceMap::new(map))
224 }
225
226 pub fn new_sectioned(sections: Vec<SourceMapSection>) -> Self {
229 SourceMap::Sectioned(SectionedSourceMap::new(sections))
230 }
231
232 pub fn new_from_rope(content: &Rope) -> Result<Option<Self>> {
233 let Ok(map) = DecodedMap::from_reader(content.read()) else {
234 return Ok(None);
235 };
236 Ok(Some(SourceMap::Decoded(InnerSourceMap::new(map))))
237 }
238
239 pub async fn new_from_file(file: FileSystemPath) -> Result<Option<Self>> {
240 let read = file.read();
241 Self::new_from_file_content(read).await
242 }
243
244 pub async fn new_from_file_content(content: Vc<FileContent>) -> Result<Option<Self>> {
245 let content = &content.await?;
246 let Some(contents) = content.as_content() else {
247 return Ok(None);
248 };
249 let Ok(map) = DecodedMap::from_reader(contents.read()) else {
250 return Ok(None);
251 };
252 Ok(Some(SourceMap::Decoded(InnerSourceMap::new(map))))
253 }
254}
255
256#[turbo_tasks::value_impl]
257impl SourceMap {
258 #[turbo_tasks::function]
261 pub async fn new_from_rope_cached(
262 content: Vc<OptionStringifiedSourceMap>,
263 ) -> Result<Vc<OptionSourceMap>> {
264 let Some(content) = &*content.await? else {
265 return Ok(OptionSourceMap::none());
266 };
267 Ok(Vc::cell(SourceMap::new_from_rope(content)?))
268 }
269}
270
271impl SourceMap {
272 pub async fn to_source_map(&self) -> Result<Arc<CrateMapWrapper>> {
273 Ok(match self {
274 Self::Decoded(m) => m.map.clone(),
275 Self::Sectioned(m) => {
276 let wrapped = m.to_crate_wrapper().await?;
277 let sections = wrapped
278 .sections
279 .iter()
280 .map(|s| {
281 swc_sourcemap::SourceMapSection::new(
282 (s.offset.line, s.offset.column),
283 None,
284 Some(s.map.0.clone()),
285 )
286 })
287 .collect::<Vec<swc_sourcemap::SourceMapSection>>();
288 Arc::new(CrateMapWrapper(DecodedMap::Index(SourceMapIndex::new(
289 None, sections,
290 ))))
291 }
292 })
293 }
294}
295
296static EMPTY_SOURCE_MAP_ROPE: Lazy<Rope> =
297 Lazy::new(|| Rope::from(r#"{"version":3,"sources":[],"names":[],"mappings":"A"}"#));
298
299impl SourceMap {
300 pub fn empty() -> Self {
305 let mut builder = SourceMapBuilder::new(None);
306 builder.add(0, 0, 0, 0, None, None, false);
307 SourceMap::new_regular(builder.into_sourcemap())
308 }
309
310 pub fn empty_rope() -> Rope {
315 EMPTY_SOURCE_MAP_ROPE.clone()
316 }
317
318 pub fn sections_to_rope(sections: impl IntoIterator<Item = (SourcePos, Rope)>) -> Result<Rope> {
319 let mut sections = sections.into_iter().peekable();
320
321 let mut first = sections.next();
322 if let Some((offset, map)) = &mut first
323 && sections.peek().is_none()
324 && *offset == (0, 0)
325 {
326 return Ok(std::mem::take(map));
328 }
329
330 let mut rope = RopeBuilder::from(
334 r#"{
335 "version": 3,
336 "sources": [],
337 "sections": ["#,
338 );
339
340 let mut first_section = true;
341 for (offset, section_map) in first.into_iter().chain(sections) {
342 if !first_section {
343 rope += ",";
344 }
345 first_section = false;
346
347 write!(
348 rope,
349 r#"
350 {{"offset": {{"line": {}, "column": {}}}, "map": "#,
351 offset.line, offset.column,
352 )?;
353
354 rope += §ion_map;
355
356 rope += "}";
357 }
358
359 rope += "]
360}";
361
362 Ok(rope.build())
363 }
364
365 pub async fn to_rope(&self) -> Result<Rope> {
367 let rope = match self {
368 SourceMap::Decoded(r) => {
369 let mut bytes = vec![];
370 r.0.to_writer(&mut bytes)?;
371 Rope::from(bytes)
372 }
373
374 SourceMap::Sectioned(s) => {
375 let sections = s
376 .sections
377 .iter()
378 .map(async |s| Ok((s.offset, s.map.to_rope().await?)))
379 .try_join()
380 .await?;
381
382 Self::sections_to_rope(sections)?
383 }
384 };
385 Ok(rope)
386 }
387
388 pub async fn lookup_token(&self, line: u32, column: u32) -> Result<Token> {
391 let (token, _) = self
392 .lookup_token_and_source_internal(line, column, true)
393 .await?;
394 Ok(token)
395 }
396
397 pub async fn lookup_token_and_source(&self, line: u32, column: u32) -> Result<TokenWithSource> {
400 let (token, content) = self
401 .lookup_token_and_source_internal(line, column, true)
402 .await?;
403 Ok(TokenWithSource {
404 token,
405 source_content: match content {
406 Some(v) => Some(v.to_resolved().await?),
407 None => None,
408 },
409 })
410 }
411
412 pub async fn with_resolved_sources(&self, origin: FileSystemPath) -> Result<Self> {
413 async fn resolve_source(
414 source_request: BytesStr,
415 source_content: Option<BytesStr>,
416 origin: FileSystemPath,
417 ) -> Result<(BytesStr, BytesStr)> {
418 Ok(
419 if let Some(path) = origin.parent().try_join(&source_request)? {
420 let path_str = path.value_to_string().await?;
421 let source = format!("{SOURCE_URL_PROTOCOL}///{path_str}");
422 let source_content = if let Some(source_content) = source_content {
423 source_content
424 } else if let FileContent::Content(file) = &*path.read().await? {
425 let text = file.content().to_str()?;
426 text.to_string().into()
427 } else {
428 format!("unable to read source {path_str}").into()
429 };
430 (source.into(), source_content)
431 } else {
432 let origin_str = origin.value_to_string().await?;
433 static INVALID_REGEX: Lazy<Regex> =
434 Lazy::new(|| Regex::new(r#"(?:^|/)(?:\.\.?(?:/|$))+"#).unwrap());
435 let source = INVALID_REGEX
436 .replace_all(&source_request, |s: ®ex::Captures<'_>| {
437 s[0].replace('.', "_")
438 });
439 let source = format!("{SOURCE_URL_PROTOCOL}///{origin_str}/{source}");
440 let source_content = source_content.unwrap_or_else(|| {
441 format!(
442 "unable to access {source_request} in {origin_str} (it's leaving the \
443 filesystem root)"
444 )
445 .into()
446 });
447 (source.into(), source_content)
448 },
449 )
450 }
451 async fn regular_map_with_resolved_sources(
452 map: &RegularMapWrapper,
453 origin: FileSystemPath,
454 ) -> Result<RegularMap> {
455 let map = &map.0;
456 let file = map.get_file().cloned();
457 let tokens = map.tokens().map(|t| t.get_raw_token()).collect();
458 let names = map.names().cloned().collect();
459 let count = map.get_source_count() as usize;
460 let sources = map.sources().cloned().collect::<Vec<_>>();
461 let source_contents = map
462 .source_contents()
463 .map(|s| s.cloned())
464 .collect::<Vec<_>>();
465 let mut new_sources = Vec::with_capacity(count);
466 let mut new_source_contents = Vec::with_capacity(count);
467 for (source, source_content) in sources.into_iter().zip(source_contents.into_iter()) {
468 let (source, name) = resolve_source(source, source_content, origin.clone()).await?;
469 new_sources.push(source);
470 new_source_contents.push(Some(name));
471 }
472 let mut map =
473 RegularMap::new(file, tokens, names, new_sources, Some(new_source_contents));
474
475 add_default_ignore_list(&mut map);
476
477 Ok(map)
478 }
479 async fn decoded_map_with_resolved_sources(
480 map: &CrateMapWrapper,
481 origin: FileSystemPath,
482 ) -> Result<CrateMapWrapper> {
483 Ok(CrateMapWrapper(match &map.0 {
484 DecodedMap::Regular(map) => {
485 let map = RegularMapWrapper::ref_cast(map);
486 DecodedMap::Regular(regular_map_with_resolved_sources(map, origin).await?)
487 }
488 DecodedMap::Index(map) => {
489 let count = map.get_section_count() as usize;
490 let file = map.get_file().cloned();
491 let sections = map
492 .sections()
493 .filter_map(|section| {
494 section
495 .get_sourcemap()
496 .map(|s| (section.get_offset(), CrateMapWrapper::ref_cast(s)))
497 })
498 .collect::<Vec<_>>();
499 let sections = sections
500 .into_iter()
501 .map(|(offset, map)| {
502 let origin = origin.clone();
503 async move {
504 Ok((
505 offset,
506 Box::pin(decoded_map_with_resolved_sources(
507 map,
508 origin.clone(),
509 ))
510 .await?,
511 ))
512 }
513 })
514 .try_join()
515 .await?;
516 let mut new_sections = Vec::with_capacity(count);
517 for (offset, map) in sections {
518 new_sections.push(swc_sourcemap::SourceMapSection::new(
519 offset,
520 None,
522 Some(map.0),
523 ));
524 }
525 DecodedMap::Index(SourceMapIndex::new(file, new_sections))
526 }
527 DecodedMap::Hermes(_) => {
528 todo!("hermes source maps are not implemented");
529 }
530 }))
531 }
532 Ok(match self {
533 Self::Decoded(m) => {
534 let map = Box::pin(decoded_map_with_resolved_sources(&m.map, origin)).await?;
535 Self::Decoded(InnerSourceMap::new(map.0))
536 }
537 Self::Sectioned(m) => {
538 let mut sections = Vec::with_capacity(m.sections.len());
539 for section in &m.sections {
540 let map = Box::pin(section.map.with_resolved_sources(origin.clone())).await?;
541 sections.push(SourceMapSection::new(section.offset, map));
542 }
543 SourceMap::new_sectioned(sections)
544 }
545 })
546 }
547}
548
549#[turbo_tasks::function]
550fn sourcemap_content_fs_root() -> Vc<FileSystemPath> {
551 VirtualFileSystem::new_with_name("sourcemap-content".into()).root()
552}
553
554#[turbo_tasks::function]
555async fn sourcemap_content_source(path: RcStr, content: RcStr) -> Result<Vc<Box<dyn Source>>> {
556 let path = sourcemap_content_fs_root().await?.join(&path)?;
557 let content = AssetContent::file(FileContent::new(File::from(content)).cell());
558 Ok(Vc::upcast(VirtualSource::new(path, content)))
559}
560
561impl SourceMap {
562 async fn lookup_token_and_source_internal(
563 &self,
564 line: u32,
565 column: u32,
566 need_source_content: bool,
567 ) -> Result<(Token, Option<Vc<Box<dyn Source>>>)> {
568 let mut content: Option<Vc<Box<dyn Source>>> = None;
569
570 let token: Token = match self {
571 SourceMap::Decoded(map) => {
572 let tok = map.lookup_token(line, column);
573 let mut token = tok.map(Token::from).unwrap_or_else(|| {
574 Token::Synthetic(SyntheticToken {
575 generated_line: line,
576 generated_column: column,
577 guessed_original_file: None,
578 })
579 });
580
581 if let Token::Synthetic(SyntheticToken {
582 guessed_original_file,
583 ..
584 }) = &mut token
585 && let DecodedMap::Regular(map) = &map.map.0
586 && map.get_source_count() == 1
587 {
588 let source = map.sources().next().unwrap().clone();
589 *guessed_original_file = Some(RcStr::from(source));
590 }
591
592 if need_source_content
593 && content.is_none()
594 && let Some(map) = map.map.as_regular_source_map()
595 {
596 content = tok.and_then(|tok| {
597 let src_id = tok.get_src_id();
598
599 let name = map.get_source(src_id);
600 let content = map.get_source_contents(src_id);
601
602 let (name, content) = name.zip(content)?;
603 Some(sourcemap_content_source(
604 name.clone().into(),
605 content.clone().into(),
606 ))
607 });
608 }
609
610 token
611 }
612
613 SourceMap::Sectioned(map) => {
614 let len = map.sections.len();
615 let mut low = 0;
616 let mut high = len;
617 let pos = SourcePos { line, column };
618
619 while low < high {
622 let mid = (low + high) / 2;
623 if pos < map.sections[mid].offset {
624 high = mid;
625 } else {
626 low = mid + 1;
627 }
628 }
629
630 if low > 0 && low <= len {
634 let SourceMapSection { map, offset } = &map.sections[low - 1];
635 let l = line - offset.line;
638 let c = if line == offset.line {
641 column - offset.column
642 } else {
643 column
644 };
645
646 if need_source_content {
647 let result = Box::pin(map.lookup_token_and_source(l, c)).await?;
648 return Ok((result.token, result.source_content.map(|v| *v)));
649 } else {
650 return Ok((Box::pin(map.lookup_token(l, c)).await?, None));
651 }
652 }
653 Token::Synthetic(SyntheticToken {
654 generated_line: line,
655 generated_column: column,
656 guessed_original_file: None,
657 })
658 }
659 };
660
661 Ok((token, content))
662 }
663}
664
665#[turbo_tasks::value_impl]
666impl GenerateSourceMap for SourceMap {
667 #[turbo_tasks::function]
668 async fn generate_source_map(self: ResolvedVc<Self>) -> Result<Vc<OptionStringifiedSourceMap>> {
669 Ok(Vc::cell(Some(self.await?.to_rope().await?)))
670 }
671}
672
673#[derive(Clone, Debug, Serialize, Deserialize)]
675pub struct InnerSourceMap {
676 map: Arc<CrateMapWrapper>,
677}
678
679impl InnerSourceMap {
680 pub fn new(map: DecodedMap) -> Self {
681 InnerSourceMap {
682 map: Arc::new(CrateMapWrapper(map)),
683 }
684 }
685}
686
687impl Deref for InnerSourceMap {
688 type Target = Arc<CrateMapWrapper>;
689
690 fn deref(&self) -> &Self::Target {
691 &self.map
692 }
693}
694
695impl Eq for InnerSourceMap {}
696impl PartialEq for InnerSourceMap {
697 fn eq(&self, other: &Self) -> bool {
698 Arc::ptr_eq(&self.map, &other.map)
699 }
700}
701
702#[derive(Debug, RefCast)]
708#[repr(transparent)]
709pub struct CrateMapWrapper(DecodedMap);
710
711unsafe impl Send for CrateMapWrapper {}
714unsafe impl Sync for CrateMapWrapper {}
715
716#[derive(Debug, RefCast)]
722#[repr(transparent)]
723pub struct RegularMapWrapper(RegularMap);
724
725unsafe impl Send for RegularMapWrapper {}
728unsafe impl Sync for RegularMapWrapper {}
729
730#[derive(Debug)]
731pub struct CrateIndexWrapper {
732 pub sections: Vec<CrateSectionWrapper>,
733}
734
735#[derive(Debug)]
736pub struct CrateSectionWrapper {
737 pub offset: SourcePos,
738 pub map: Arc<CrateMapWrapper>,
739}
740
741impl CrateMapWrapper {
742 pub fn as_regular_source_map(&self) -> Option<Cow<'_, RegularMap>> {
743 match &self.0 {
744 DecodedMap::Regular(m) => Some(Cow::Borrowed(m)),
745 DecodedMap::Index(m) => m.flatten().map(Cow::Owned).ok(),
746 _ => None,
747 }
748 }
749}
750
751impl Deref for CrateMapWrapper {
752 type Target = DecodedMap;
753
754 fn deref(&self) -> &Self::Target {
755 &self.0
756 }
757}
758
759impl Serialize for CrateMapWrapper {
760 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
761 use serde::ser::Error;
762 let mut bytes = vec![];
763 self.0.to_writer(&mut bytes).map_err(Error::custom)?;
764 serializer.serialize_bytes(bytes.as_slice())
765 }
766}
767
768impl<'de> Deserialize<'de> for CrateMapWrapper {
769 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
770 use serde::de::Error;
771 let bytes = <&[u8]>::deserialize(deserializer)?;
772 let map = DecodedMap::from_reader(bytes).map_err(Error::custom)?;
773 Ok(CrateMapWrapper(map))
774 }
775}
776
777#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
780pub struct SectionedSourceMap {
781 sections: Vec<SourceMapSection>,
782}
783
784impl SectionedSourceMap {
785 pub fn new(sections: Vec<SourceMapSection>) -> Self {
786 Self { sections }
787 }
788
789 pub async fn to_crate_wrapper(&self) -> Result<CrateIndexWrapper> {
790 let mut sections = Vec::with_capacity(self.sections.len());
791 for section in &self.sections {
792 sections.push(section.to_crate_wrapper().await?);
793 }
794 Ok(CrateIndexWrapper { sections })
795 }
796}
797
798#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
801pub struct SourceMapSection {
802 offset: SourcePos,
803 map: SourceMap,
804}
805
806impl SourceMapSection {
807 pub fn new(offset: SourcePos, map: SourceMap) -> Self {
808 Self { offset, map }
809 }
810
811 pub async fn to_crate_wrapper(&self) -> Result<CrateSectionWrapper> {
812 let map = Box::pin(self.map.to_source_map()).await?;
813 Ok(CrateSectionWrapper {
814 offset: self.offset,
815 map,
816 })
817 }
818}