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