turbopack_ecmascript/references/
raw.rs1use 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::{ChunkableModuleReference, ChunkingType, ChunkingTypeOption},
8 file_source::FileSource,
9 issue::{
10 Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
11 OptionStyledString, StyledString,
12 },
13 raw_module::RawModule,
14 reference::ModuleReference,
15 resolve::{
16 ModuleResolveResult, RequestKey,
17 pattern::{Pattern, PatternMatch, read_matches},
18 resolve_raw,
19 },
20};
21#[turbo_tasks::value]
22#[derive(Hash, Debug)]
23pub struct FileSourceReference {
24 context_dir: FileSystemPath,
25 path: ResolvedVc<Pattern>,
26 collect_affecting_sources: bool,
27 issue_source: IssueSource,
28}
29
30#[turbo_tasks::value_impl]
31impl FileSourceReference {
32 #[turbo_tasks::function]
33 pub fn new(
34 context_dir: FileSystemPath,
35 path: ResolvedVc<Pattern>,
36 collect_affecting_sources: bool,
37 issue_source: IssueSource,
38 ) -> Vc<Self> {
39 Self::cell(FileSourceReference {
40 context_dir,
41 path,
42 collect_affecting_sources,
43 issue_source,
44 })
45 }
46}
47
48#[turbo_tasks::value_impl]
49impl ModuleReference for FileSourceReference {
50 #[turbo_tasks::function]
51 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
52 let span = tracing::info_span!(
53 "trace file",
54 pattern = display(self.path.to_string().await?)
55 );
56 async {
57 let result = resolve_raw(
58 self.context_dir.clone(),
59 *self.path,
60 self.collect_affecting_sources,
61 false,
62 )
63 .as_raw_module_result()
64 .resolve()
65 .await?;
66 check_and_emit_too_many_matches_warning(
67 result,
68 self.issue_source,
69 self.context_dir.clone(),
70 self.path,
71 )
72 .await?;
73
74 Ok(result)
75 }
76 .instrument(span)
77 .await
78 }
79}
80#[turbo_tasks::value_impl]
81impl ChunkableModuleReference for FileSourceReference {
82 #[turbo_tasks::function]
83 fn chunking_type(&self) -> Vc<ChunkingTypeOption> {
84 Vc::cell(Some(ChunkingType::Traced))
85 }
86}
87
88#[turbo_tasks::value_impl]
89impl ValueToString for FileSourceReference {
90 #[turbo_tasks::function]
91 async fn to_string(&self) -> Result<Vc<RcStr>> {
92 Ok(Vc::cell(
93 format!("raw asset {}", self.path.to_string().await?,).into(),
94 ))
95 }
96}
97
98#[turbo_tasks::value]
99#[derive(Hash, Debug)]
100pub struct DirAssetReference {
101 context_dir: FileSystemPath,
102 path: ResolvedVc<Pattern>,
103 issue_source: IssueSource,
104}
105
106#[turbo_tasks::value_impl]
107impl DirAssetReference {
108 #[turbo_tasks::function]
109 pub fn new(
110 context_dir: FileSystemPath,
111 path: ResolvedVc<Pattern>,
112 issue_source: IssueSource,
113 ) -> Vc<Self> {
114 Self::cell(DirAssetReference {
115 context_dir,
116 path,
117 issue_source,
118 })
119 }
120}
121
122async fn resolve_reference_from_dir(
123 context_dir: FileSystemPath,
124 path: Vc<Pattern>,
125) -> Result<Vc<ModuleResolveResult>> {
126 let path_ref = path.await?;
127 let (abs_path, rel_path) = path_ref.split_could_match("/ROOT/");
128 if abs_path.is_none() && rel_path.is_none() {
129 return Ok(*ModuleResolveResult::unresolvable());
130 }
131
132 let abs_matches = if let Some(abs_path) = &abs_path {
133 Some(
134 read_matches(
135 context_dir.root().owned().await?,
136 rcstr!("/ROOT/"),
137 true,
138 Pattern::new(abs_path.or_any_nested_file()),
139 )
140 .await?,
141 )
142 } else {
143 None
144 };
145 let rel_matches = if let Some(rel_path) = &rel_path {
146 Some(
147 read_matches(
148 context_dir,
149 rcstr!(""),
150 true,
151 Pattern::new(rel_path.or_any_nested_file()),
152 )
153 .await?,
154 )
155 } else {
156 None
157 };
158
159 let matches = abs_matches
160 .into_iter()
161 .flatten()
162 .chain(rel_matches.into_iter().flatten());
163
164 let mut affecting_sources = Vec::new();
165 let mut results = Vec::new();
166 for pat_match in matches {
167 match pat_match {
168 PatternMatch::File(matched_path, file) => {
169 let realpath = file.realpath_with_links().await?;
170 for symlink in &realpath.symlinks {
171 affecting_sources.push(ResolvedVc::upcast(
172 FileSource::new(symlink.clone()).to_resolved().await?,
173 ));
174 }
175 let path: FileSystemPath = match &realpath.path_result {
176 Ok(path) => path.clone(),
177 Err(e) => bail!(e.as_error_message(file, &realpath)),
178 };
179 results.push((
180 RequestKey::new(matched_path.clone()),
181 ResolvedVc::upcast(
182 RawModule::new(Vc::upcast(FileSource::new(path)))
183 .to_resolved()
184 .await?,
185 ),
186 ));
187 }
188 PatternMatch::Directory(..) => {}
189 }
190 }
191 Ok(*ModuleResolveResult::modules_with_affecting_sources(
192 results,
193 affecting_sources,
194 ))
195}
196
197#[turbo_tasks::value_impl]
198impl ModuleReference for DirAssetReference {
199 #[turbo_tasks::function]
200 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
201 let span = tracing::info_span!(
202 "trace directory",
203 pattern = display(self.path.to_string().await?)
204 );
205 async {
206 let result = resolve_reference_from_dir(self.context_dir.clone(), *self.path).await?;
207 check_and_emit_too_many_matches_warning(
208 result,
209 self.issue_source,
210 self.context_dir.clone(),
211 self.path,
212 )
213 .await?;
214 Ok(result)
215 }
216 .instrument(span)
217 .await
218 }
219}
220
221#[turbo_tasks::value_impl]
222impl ChunkableModuleReference for DirAssetReference {
223 #[turbo_tasks::function]
224 fn chunking_type(&self) -> Vc<ChunkingTypeOption> {
225 Vc::cell(Some(ChunkingType::Traced))
226 }
227}
228
229#[turbo_tasks::value_impl]
230impl ValueToString for DirAssetReference {
231 #[turbo_tasks::function]
232 async fn to_string(&self) -> Result<Vc<RcStr>> {
233 Ok(Vc::cell(
234 format!("directory assets {}", self.path.to_string().await?,).into(),
235 ))
236 }
237}
238
239const TOO_MANY_MATCHES_LIMIT: usize = 10000;
241
242async fn check_and_emit_too_many_matches_warning(
243 result: Vc<ModuleResolveResult>,
244 issue_source: IssueSource,
245 context_dir: FileSystemPath,
246 pattern: ResolvedVc<Pattern>,
247) -> Result<()> {
248 let num_matches = result.await?.primary.len();
249 if num_matches > TOO_MANY_MATCHES_LIMIT {
250 TooManyMatchesWarning {
251 source: issue_source,
252 context_dir,
253 num_matches,
254 pattern,
255 }
256 .resolved_cell()
257 .emit();
258 }
259 Ok(())
260}
261
262#[turbo_tasks::value(shared)]
263struct TooManyMatchesWarning {
264 source: IssueSource,
265 context_dir: FileSystemPath,
266 num_matches: usize,
267 pattern: ResolvedVc<Pattern>,
268}
269
270#[turbo_tasks::value_impl]
271impl Issue for TooManyMatchesWarning {
272 #[turbo_tasks::function]
273 async fn title(&self) -> Result<Vc<StyledString>> {
274 Ok(StyledString::Text(
275 format!(
276 "The file pattern {pattern} matches {num_matches} files in {context_dir}",
277 pattern = self.pattern.to_string().await?,
278 context_dir = self.context_dir.value_to_string().await?,
279 num_matches = self.num_matches
280 )
281 .into(),
282 )
283 .cell())
284 }
285
286 #[turbo_tasks::function]
287 fn description(&self) -> Vc<OptionStyledString> {
288 Vc::cell(Some(
289 StyledString::Text(rcstr!(
290 "Overly broad patterns can lead to build performance issues and over bundling."
291 ))
292 .resolved_cell(),
293 ))
294 }
295
296 #[turbo_tasks::function]
297 async fn file_path(&self) -> Vc<FileSystemPath> {
298 self.source.file_path()
299 }
300
301 #[turbo_tasks::function]
302 fn stage(&self) -> Vc<IssueStage> {
303 IssueStage::Resolve.cell()
304 }
305
306 fn severity(&self) -> IssueSeverity {
307 IssueSeverity::Warning
308 }
309
310 #[turbo_tasks::function]
311 fn source(&self) -> Vc<OptionIssueSource> {
312 Vc::cell(Some(self.source))
313 }
314}