Skip to main content

turbopack_core/issue/
resolve.rs

1use std::fmt::Write;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{PrettyPrintError, ReadRef, ResolvedVc, ValueToString, ValueToStringRef, Vc};
7use turbo_tasks_fs::FileSystemPath;
8
9use super::{Issue, IssueSource, IssueStage, StyledString};
10use crate::{
11    issue::IssueSeverity,
12    resolve::{
13        options::{ImportMap, ImportMapResult, ResolveOptions},
14        parse::Request,
15    },
16};
17
18#[turbo_tasks::value(shared)]
19pub struct ResolvingIssue {
20    pub severity: IssueSeverity,
21    pub request_type: String,
22    pub request: ResolvedVc<Request>,
23    pub file_path: FileSystemPath,
24    pub resolve_options: ResolvedVc<ResolveOptions>,
25    pub error_message: Option<String>,
26    pub source: Option<IssueSource>,
27}
28
29#[async_trait]
30#[turbo_tasks::value_impl]
31impl Issue for ResolvingIssue {
32    fn severity(&self) -> IssueSeverity {
33        self.severity
34    }
35
36    async fn title(&self) -> Result<StyledString> {
37        let request = self.request.request_pattern().to_string().owned().await?;
38        Ok(StyledString::Line(vec![
39            StyledString::Strong(rcstr!("Module not found")),
40            StyledString::Text(rcstr!(": Can't resolve ")),
41            StyledString::Code(request),
42        ]))
43    }
44
45    fn stage(&self) -> IssueStage {
46        IssueStage::Resolve
47    }
48
49    async fn file_path(&self) -> Result<FileSystemPath> {
50        Ok(self.file_path.clone())
51    }
52
53    async fn description(&self) -> Result<Option<StyledString>> {
54        let mut description = String::new();
55        if let Some(error_message) = &self.error_message {
56            writeln!(description, "{error_message}")?;
57        }
58        let request_value = self.request.await?;
59        let request_parts = match &*request_value {
60            Request::Alternatives { requests } => requests.as_slice(),
61            _ => &[self.request],
62        };
63
64        if let Some(import_map) = &self.resolve_options.await?.import_map {
65            for req in request_parts {
66                match lookup_import_map(**import_map, self.file_path.clone(), **req).await {
67                    Ok(None) => {}
68                    Ok(Some(str)) => writeln!(description, "Import map: {str}")?,
69                    Err(err) => {
70                        writeln!(
71                            description,
72                            "Error while looking up import map: {}",
73                            PrettyPrintError(&err)
74                        )?;
75                    }
76                }
77            }
78        }
79        Ok(Some(StyledString::Text(description.into())))
80    }
81
82    async fn detail(&self) -> Result<Option<StyledString>> {
83        let mut detail = String::new();
84
85        if self.error_message.is_some() {
86            writeln!(detail, "An error happened during resolving.")?;
87        } else {
88            writeln!(detail, "It was not possible to find the requested file.")?;
89        }
90        writeln!(
91            detail,
92            "Parsed request as written in source code: {request}",
93            request = self.request.to_string().await?
94        )?;
95        writeln!(
96            detail,
97            "Path where resolving has started: {context}",
98            context = self.file_path.to_string_ref().await?
99        )?;
100        writeln!(
101            detail,
102            "Type of request: {request_type}",
103            request_type = self.request_type,
104        )?;
105        Ok(Some(StyledString::Text(detail.into())))
106    }
107
108    fn source(&self) -> Option<IssueSource> {
109        self.source
110    }
111
112    // TODO add sub_issue for a description of resolve_options
113    // TODO add source link
114}
115
116async fn lookup_import_map(
117    import_map: Vc<ImportMap>,
118    file_path: FileSystemPath,
119    request: Vc<Request>,
120) -> Result<Option<ReadRef<RcStr>>> {
121    let result = import_map.await?.lookup(file_path, request).await?;
122
123    if matches!(result, ImportMapResult::NoEntry) {
124        return Ok(None);
125    }
126    Ok(Some(result.cell().to_string().await?))
127}