Skip to main content

turbopack_core/issue/
resolve.rs

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