Skip to main content

turbopack_ecmascript/references/
raw.rs

1use anyhow::{Result, bail};
2use tracing::Instrument;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, ValueToString, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_core::{
7    chunk::{ChunkingType, TracedMode},
8    file_source::FileSource,
9    issue::IssueSource,
10    raw_module::RawModule,
11    reference::{DynamicTraceReference, ModuleReference},
12    resolve::{
13        ModuleResolveResult, RequestKey,
14        pattern::{Pattern, PatternMatch, read_matches},
15        resolve_raw,
16    },
17};
18
19use crate::references::util::check_and_emit_too_many_matches_warning;
20
21#[turbo_tasks::value]
22#[derive(Hash, Debug, ValueToString)]
23#[value_to_string("raw asset {path}")]
24pub struct FileSourceReference {
25    context_dir: FileSystemPath,
26    path: ResolvedVc<Pattern>,
27    collect_affecting_sources: bool,
28    issue_source: IssueSource,
29    /// The dynamic function whose access triggered this reference (e.g.
30    /// `fs.readFileSync`), used to name the call in diagnostics.
31    origin_fn_name: RcStr,
32}
33
34#[turbo_tasks::value_impl]
35impl FileSourceReference {
36    #[turbo_tasks::function]
37    pub fn new(
38        context_dir: FileSystemPath,
39        path: ResolvedVc<Pattern>,
40        collect_affecting_sources: bool,
41        issue_source: IssueSource,
42        origin_fn_name: RcStr,
43    ) -> Vc<Self> {
44        Self::cell(FileSourceReference {
45            context_dir,
46            path,
47            collect_affecting_sources,
48            issue_source,
49            origin_fn_name,
50        })
51    }
52}
53
54#[turbo_tasks::value_impl]
55impl ModuleReference for FileSourceReference {
56    #[turbo_tasks::function]
57    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
58        let span = tracing::info_span!(
59            "trace file",
60            pattern = display(self.path.to_string().await?)
61        );
62        async {
63            let result = resolve_raw(
64                self.context_dir.clone(),
65                *self.path,
66                self.collect_affecting_sources,
67                /* force_in_lookup_dir */ false,
68            )
69            .as_raw_module_result()
70            .to_resolved()
71            .await?;
72            check_and_emit_too_many_matches_warning(
73                *result,
74                self.issue_source,
75                self.context_dir.clone(),
76                self.path,
77            )
78            .await?;
79
80            Ok(*result)
81        }
82        .instrument(span)
83        .await
84    }
85
86    fn chunking_type(&self) -> Option<ChunkingType> {
87        Some(ChunkingType::Traced {
88            mode: TracedMode::Entry,
89        })
90    }
91
92    fn source(&self) -> Option<IssueSource> {
93        Some(self.issue_source)
94    }
95}
96
97#[turbo_tasks::value_impl]
98impl DynamicTraceReference for FileSourceReference {
99    fn origin_fn_name(&self) -> RcStr {
100        self.origin_fn_name.clone()
101    }
102}
103
104#[turbo_tasks::value]
105#[derive(Hash, Debug, ValueToString)]
106#[value_to_string("directory assets {path}")]
107pub struct DirAssetReference {
108    context_dir: FileSystemPath,
109    path: ResolvedVc<Pattern>,
110    issue_source: IssueSource,
111    /// The dynamic function whose access triggered this reference (e.g.
112    /// `fs.readdir`), used to name the call in diagnostics.
113    origin_fn_name: RcStr,
114}
115
116#[turbo_tasks::value_impl]
117impl DirAssetReference {
118    #[turbo_tasks::function]
119    pub fn new(
120        context_dir: FileSystemPath,
121        path: ResolvedVc<Pattern>,
122        issue_source: IssueSource,
123        origin_fn_name: RcStr,
124    ) -> Vc<Self> {
125        Self::cell(DirAssetReference {
126            context_dir,
127            path,
128            issue_source,
129            origin_fn_name,
130        })
131    }
132}
133
134async fn resolve_reference_from_dir(
135    context_dir: FileSystemPath,
136    path: Vc<Pattern>,
137) -> Result<Vc<ModuleResolveResult>> {
138    let path_ref = path.await?;
139    let (abs_path, rel_path) = path_ref.split_could_match("/ROOT/");
140    if abs_path.is_none() && rel_path.is_none() {
141        return Ok(*ModuleResolveResult::unresolvable());
142    }
143
144    let abs_matches = if let Some(abs_path) = &abs_path {
145        Some(
146            read_matches(
147                context_dir.root().owned().await?,
148                rcstr!("/ROOT/"),
149                true,
150                Pattern::new(abs_path.or_any_nested_file()),
151            )
152            .await?,
153        )
154    } else {
155        None
156    };
157    let rel_matches = if let Some(rel_path) = &rel_path {
158        Some(
159            read_matches(
160                context_dir,
161                rcstr!(""),
162                true,
163                Pattern::new(rel_path.or_any_nested_file()),
164            )
165            .await?,
166        )
167    } else {
168        None
169    };
170
171    let matches = abs_matches
172        .iter()
173        .flatten()
174        .chain(rel_matches.iter().flatten());
175
176    let mut affecting_sources = Vec::new();
177    let mut results = Vec::new();
178    for pat_match in matches {
179        match pat_match {
180            PatternMatch::File(matched_path, file) => {
181                let realpath = file.realpath_with_links().await?;
182                for symlink in &realpath.symlinks {
183                    affecting_sources.push(ResolvedVc::upcast(
184                        FileSource::new(symlink.clone()).to_resolved().await?,
185                    ));
186                }
187                let path: FileSystemPath = match &realpath.path_result {
188                    Ok(path) => path.clone(),
189                    Err(error) => bail!(error.clone()),
190                };
191                results.push((
192                    RequestKey::new(matched_path.clone()),
193                    ResolvedVc::upcast(
194                        RawModule::new(Vc::upcast(FileSource::new(path.clone())))
195                            .to_resolved()
196                            .await?,
197                    ),
198                ));
199            }
200            PatternMatch::Directory(..) => {}
201        }
202    }
203    Ok(*ModuleResolveResult::modules_with_affecting_sources(
204        results,
205        affecting_sources,
206    ))
207}
208
209#[turbo_tasks::value_impl]
210impl ModuleReference for DirAssetReference {
211    #[turbo_tasks::function]
212    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
213        let span = tracing::info_span!(
214            "trace directory",
215            pattern = display(self.path.to_string().await?)
216        );
217        async {
218            let result = resolve_reference_from_dir(self.context_dir.clone(), *self.path).await?;
219            check_and_emit_too_many_matches_warning(
220                result,
221                self.issue_source,
222                self.context_dir.clone(),
223                self.path,
224            )
225            .await?;
226            Ok(result)
227        }
228        .instrument(span)
229        .await
230    }
231
232    fn chunking_type(&self) -> Option<ChunkingType> {
233        Some(ChunkingType::Traced {
234            mode: TracedMode::Entry,
235        })
236    }
237
238    fn source(&self) -> Option<IssueSource> {
239        Some(self.issue_source)
240    }
241}
242
243#[turbo_tasks::value_impl]
244impl DynamicTraceReference for DirAssetReference {
245    fn origin_fn_name(&self) -> RcStr {
246        self.origin_fn_name.clone()
247    }
248}