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