turbopack_core/issue/
resolve.rs

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