1use std::mem::take;
2
3use anyhow::{Context, Result, bail};
4use base64::Engine;
5use bincode::{Decode, Encode};
6use either::Either;
7use futures::try_join;
8use serde::{Deserialize, Serialize};
9use serde_json::{Map as JsonMap, Value as JsonValue, json};
10use serde_with::serde_as;
11use tracing::Instrument;
12use turbo_rcstr::{RcStr, rcstr};
13use turbo_tasks::{
14 Completion, OperationVc, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt, Vc,
15 trace::TraceRawVcs, turbobail,
16};
17use turbo_tasks_env::ProcessEnv;
18use turbo_tasks_fs::{
19 File, FileContent, FileSystemPath,
20 glob::{Glob, GlobOptions},
21 json::parse_json_with_source_context,
22 rope::Rope,
23};
24use turbopack_core::{
25 asset::{Asset, AssetContent},
26 chunk::ChunkingContext,
27 context::{AssetContext, ProcessResult},
28 file_source::FileSource,
29 ident::AssetIdent,
30 issue::{
31 Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
32 OptionStyledString, StyledString,
33 },
34 module_graph::{ModuleGraph, SingleModuleGraph},
35 reference_type::{InnerAssets, ReferenceType},
36 resolve::{
37 options::{ConditionValue, ResolveInPackage, ResolveIntoPackage, ResolveOptions},
38 parse::Request,
39 pattern::Pattern,
40 resolve,
41 },
42 source::Source,
43 source_map::{GenerateSourceMap, utils::resolve_source_map_sources},
44 source_transform::SourceTransform,
45 virtual_source::VirtualSource,
46};
47use turbopack_resolve::{
48 ecmascript::get_condition_maps, resolve::resolve_options,
49 resolve_options_context::ResolveOptionsContext,
50};
51
52use crate::{
53 AssetsForSourceMapping,
54 backend::NodeBackend,
55 debug::should_debug,
56 embed_js::embed_file_path,
57 evaluate::{
58 EnvVarTracking, EvaluateContext, EvaluateEntries, EvaluatePool, EvaluationIssue,
59 custom_evaluate, get_evaluate_entries, get_evaluate_pool,
60 },
61 execution_context::ExecutionContext,
62 format::FormattingMode,
63 source_map::{StackFrame, StructuredError},
64 transforms::util::{EmittedAsset, emitted_assets_to_virtual_sources},
65};
66
67#[serde_as]
68#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Encode, Decode)]
69struct BytesBase64 {
70 #[serde_as(as = "serde_with::base64::Base64")]
71 binary: Vec<u8>,
72}
73
74#[derive(Debug, Clone, Deserialize)]
75#[turbo_tasks::value]
76#[serde(rename_all = "camelCase")]
77struct WebpackLoadersProcessingResult {
78 #[serde(with = "either::serde_untagged")]
79 #[bincode(with = "turbo_bincode::either")]
80 #[turbo_tasks(debug_ignore, trace_ignore)]
81 source: Either<RcStr, BytesBase64>,
82 map: Option<RcStr>,
83 #[turbo_tasks(trace_ignore)]
84 assets: Option<Vec<EmittedAsset>>,
85}
86
87pub use turbopack_core::loader::{WebpackLoaderItem, WebpackLoaderItems};
88
89#[turbo_tasks::value]
90pub struct WebpackLoaders {
91 evaluate_context: ResolvedVc<Box<dyn AssetContext>>,
92 execution_context: ResolvedVc<ExecutionContext>,
93 loaders: ResolvedVc<WebpackLoaderItems>,
94 rename_as: Option<RcStr>,
95 resolve_options_context: ResolvedVc<ResolveOptionsContext>,
96 source_maps: bool,
97}
98
99#[turbo_tasks::value_impl]
100impl WebpackLoaders {
101 #[turbo_tasks::function]
102 pub fn new(
103 evaluate_context: ResolvedVc<Box<dyn AssetContext>>,
104 execution_context: ResolvedVc<ExecutionContext>,
105 loaders: ResolvedVc<WebpackLoaderItems>,
106 rename_as: Option<RcStr>,
107 resolve_options_context: ResolvedVc<ResolveOptionsContext>,
108 source_maps: bool,
109 ) -> Vc<Self> {
110 WebpackLoaders {
111 evaluate_context,
112 execution_context,
113 loaders,
114 rename_as,
115 resolve_options_context,
116 source_maps,
117 }
118 .cell()
119 }
120}
121
122#[turbo_tasks::value_impl]
123impl SourceTransform for WebpackLoaders {
124 #[turbo_tasks::function]
125 fn transform(
126 self: ResolvedVc<Self>,
127 source: ResolvedVc<Box<dyn Source>>,
128 ) -> Vc<Box<dyn Source>> {
129 Vc::upcast(
130 WebpackLoadersProcessedAsset {
131 transform: self,
132 source,
133 }
134 .cell(),
135 )
136 }
137}
138
139#[turbo_tasks::value]
140struct WebpackLoadersProcessedAsset {
141 transform: ResolvedVc<WebpackLoaders>,
142 source: ResolvedVc<Box<dyn Source>>,
143}
144
145#[turbo_tasks::value_impl]
146impl Source for WebpackLoadersProcessedAsset {
147 #[turbo_tasks::function]
148 async fn ident(&self) -> Result<Vc<AssetIdent>> {
149 Ok(
150 if let Some(rename_as) = self.transform.await?.rename_as.as_deref() {
151 self.source.ident().rename_as(rename_as.into())
152 } else {
153 self.source.ident()
154 },
155 )
156 }
157}
158
159#[turbo_tasks::value_impl]
160impl Asset for WebpackLoadersProcessedAsset {
161 #[turbo_tasks::function]
162 async fn content(self: Vc<Self>) -> Result<Vc<AssetContent>> {
163 Ok(*self.process().await?.content)
164 }
165}
166
167#[turbo_tasks::value_impl]
168impl GenerateSourceMap for WebpackLoadersProcessedAsset {
169 #[turbo_tasks::function]
170 async fn generate_source_map(self: Vc<Self>) -> Result<Vc<FileContent>> {
171 Ok(*self.process().await?.source_map)
172 }
173}
174
175#[turbo_tasks::value]
176struct ProcessWebpackLoadersResult {
177 content: ResolvedVc<AssetContent>,
178 source_map: ResolvedVc<FileContent>,
179 assets: Vec<ResolvedVc<VirtualSource>>,
180}
181
182#[turbo_tasks::function]
183async fn webpack_loaders_executor(
184 evaluate_context: Vc<Box<dyn AssetContext>>,
185) -> Result<Vc<ProcessResult>> {
186 Ok(evaluate_context.process(
187 Vc::upcast(FileSource::new(
188 embed_file_path(rcstr!("transforms/webpack-loaders.ts"))
189 .owned()
190 .await?,
191 )),
192 ReferenceType::Internal(InnerAssets::empty().to_resolved().await?),
193 ))
194}
195
196#[turbo_tasks::value_impl]
197impl WebpackLoadersProcessedAsset {
198 #[turbo_tasks::function]
199 async fn process(&self) -> Result<Vc<ProcessWebpackLoadersResult>> {
200 let transform = self.transform.await?;
201 let loaders = transform.loaders.await?;
202
203 let webpack_span = tracing::info_span!(
204 "webpack loader",
205 name = display(ReadRef::<WebpackLoaderItems>::as_raw_ref(&loaders))
206 );
207
208 async {
209 let ExecutionContext {
210 project_path,
211 chunking_context,
212 env,
213 node_backend,
214 } = &*transform.execution_context.await?;
215 let source_content = self.source.content();
216 let AssetContent::File(file) = *source_content.await? else {
217 bail!("Webpack Loaders transform only support transforming files");
218 };
219 let FileContent::Content(file_content) = &*file.await? else {
220 return Ok(ProcessWebpackLoadersResult {
221 content: AssetContent::File(FileContent::NotFound.resolved_cell())
222 .resolved_cell(),
223 assets: Vec::new(),
224 source_map: FileContent::NotFound.resolved_cell(),
225 }
226 .cell());
227 };
228
229 let content: JsonValue = match file_content.content().to_str() {
232 Ok(utf8_str) => utf8_str.to_string().into(),
233 Err(_) => JsonValue::Object(JsonMap::from_iter(std::iter::once((
234 "binary".to_string(),
235 JsonValue::from(
236 base64::engine::general_purpose::STANDARD
237 .encode(file_content.content().to_bytes()),
238 ),
239 )))),
240 };
241 let evaluate_context = transform.evaluate_context;
242
243 let webpack_loaders_executor = webpack_loaders_executor(*evaluate_context).module();
244
245 let entries = get_evaluate_entries(
246 webpack_loaders_executor,
247 *evaluate_context,
248 **node_backend,
249 None,
250 )
251 .to_resolved()
252 .await?;
253
254 let module_graph = ModuleGraph::from_single_graph(SingleModuleGraph::new_with_entries(
255 entries.graph_entries().to_resolved().await?,
256 false,
257 false,
258 ))
259 .connect()
260 .to_resolved()
261 .await?;
262
263 let resource_fs_path = self.source.ident().path().await?;
264 let Some(resource_path) = project_path.get_relative_path_to(&resource_fs_path) else {
265 turbobail!(
266 "Resource path \"{resource_fs_path}\" needs to be on project filesystem \
267 \"{project_path}\"",
268 );
269 };
270 let config_value = evaluate_webpack_loader(WebpackLoaderContext {
271 entries,
272 cwd: project_path.clone(),
273 env: *env,
274 node_backend: *node_backend,
275 context_source_for_issue: self.source,
276 chunking_context: *chunking_context,
277 module_graph,
278 resolve_options_context: Some(transform.resolve_options_context),
279 args: vec![
280 ResolvedVc::cell(content),
281 ResolvedVc::cell(resource_path.to_string().into()),
283 ResolvedVc::cell(self.source.ident().await?.query.to_string().into()),
284 ResolvedVc::cell(json!(*loaders)),
285 ResolvedVc::cell(transform.source_maps.into()),
286 ],
287 additional_invalidation: Completion::immutable().to_resolved().await?,
288 })
289 .await?;
290
291 let Some(val) = &*config_value else {
292 return Ok(ProcessWebpackLoadersResult {
294 content: AssetContent::File(FileContent::NotFound.resolved_cell())
295 .resolved_cell(),
296 assets: Vec::new(),
297 source_map: FileContent::NotFound.resolved_cell(),
298 }
299 .cell());
300 };
301 let processed: WebpackLoadersProcessingResult = parse_json_with_source_context(val)
302 .context(
303 "Unable to deserializate response from webpack loaders transform operation",
304 )?;
305
306 let source_map = if !transform.source_maps {
308 None
309 } else {
310 processed
311 .map
312 .map(|source_map| Rope::from(source_map.into_owned()))
313 };
314 let source_map =
315 resolve_source_map_sources(source_map.as_ref(), &resource_fs_path).await?;
316
317 let file = match processed.source {
318 Either::Left(str) => File::from(str),
319 Either::Right(bytes) => File::from(bytes.binary),
320 };
321 let assets = emitted_assets_to_virtual_sources(processed.assets).await?;
322
323 let content =
324 AssetContent::File(FileContent::Content(file).resolved_cell()).resolved_cell();
325 Ok(ProcessWebpackLoadersResult {
326 content,
327 assets,
328 source_map: if let Some(source_map) = source_map {
329 FileContent::Content(File::from(source_map)).resolved_cell()
330 } else {
331 FileContent::NotFound.resolved_cell()
332 },
333 }
334 .cell())
335 }
336 .instrument(webpack_span)
337 .await
338 }
339}
340
341#[turbo_tasks::function]
342pub(crate) async fn evaluate_webpack_loader(
343 webpack_loader_context: WebpackLoaderContext,
344) -> Result<Vc<Option<RcStr>>> {
345 custom_evaluate(webpack_loader_context).await
346}
347
348#[derive(Deserialize, Debug, PartialEq, Eq, Encode, Decode)]
349#[serde(rename_all = "camelCase")]
350enum LogType {
351 Error,
352 Warn,
353 Info,
354 Log,
355 Debug,
356 Trace,
357 Group,
358 GroupCollapsed,
359 GroupEnd,
360 Profile,
361 ProfileEnd,
362 Time,
363 Clear,
364 Status,
365}
366
367#[derive(Deserialize, Debug, PartialEq, Eq, Encode, Decode)]
368#[serde(rename_all = "camelCase")]
369pub struct LogInfo {
370 time: u64,
371 log_type: LogType,
372 #[bincode(with = "turbo_bincode::serde_self_describing")]
373 args: Vec<JsonValue>,
374 trace: Option<Vec<StackFrame<'static>>>,
375}
376
377#[derive(Deserialize, Debug)]
378#[serde(tag = "type", rename_all = "camelCase")]
379pub enum InfoMessage {
380 #[serde(rename_all = "camelCase")]
384 Dependencies {
385 #[serde(default)]
386 env_variables: Vec<RcStr>,
387 #[serde(default)]
388 file_paths: Vec<RcStr>,
389 #[serde(default)]
390 directories: Vec<(RcStr, RcStr)>,
391 #[serde(default)]
392 build_file_paths: Vec<RcStr>,
393 },
394 EmittedError {
395 severity: IssueSeverity,
396 error: StructuredError,
397 },
398 Log {
399 logs: Vec<LogInfo>,
400 },
401}
402
403#[derive(
404 Debug, Clone, TaskInput, Hash, PartialEq, Eq, Deserialize, TraceRawVcs, Encode, Decode,
405)]
406#[serde(rename_all = "camelCase")]
407pub struct WebpackResolveOptions {
408 alias_fields: Option<Vec<RcStr>>,
409 condition_names: Option<Vec<RcStr>>,
410 no_package_json: bool,
411 extensions: Option<Vec<RcStr>>,
412 main_fields: Option<Vec<RcStr>>,
413 no_exports_field: bool,
414 main_files: Option<Vec<RcStr>>,
415 no_modules: bool,
416 prefer_relative: bool,
417}
418
419#[derive(Deserialize, Debug)]
420#[serde(tag = "type", rename_all = "camelCase")]
421pub enum RequestMessage {
422 #[serde(rename_all = "camelCase")]
423 Resolve {
424 options: WebpackResolveOptions,
425 lookup_path: RcStr,
426 request: RcStr,
427 },
428 #[serde(rename_all = "camelCase")]
429 TrackFileRead { file: RcStr },
430}
431
432#[derive(Serialize, Debug)]
433#[serde(untagged)]
434pub enum ResponseMessage {
435 Resolve { path: RcStr },
436 TrackFileRead {},
438}
439
440#[derive(Clone, PartialEq, Eq, Hash, TaskInput, Debug, TraceRawVcs, Encode, Decode)]
441pub struct WebpackLoaderContext {
442 pub entries: ResolvedVc<EvaluateEntries>,
443 pub cwd: FileSystemPath,
444 pub env: ResolvedVc<Box<dyn ProcessEnv>>,
445 pub node_backend: ResolvedVc<Box<dyn NodeBackend>>,
446 pub context_source_for_issue: ResolvedVc<Box<dyn Source>>,
447 pub module_graph: ResolvedVc<ModuleGraph>,
448 pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
449 pub resolve_options_context: Option<ResolvedVc<ResolveOptionsContext>>,
450 pub args: Vec<ResolvedVc<JsonValue>>,
451 pub additional_invalidation: ResolvedVc<Completion>,
452}
453
454impl EvaluateContext for WebpackLoaderContext {
455 type InfoMessage = InfoMessage;
456 type RequestMessage = RequestMessage;
457 type ResponseMessage = ResponseMessage;
458 type State = Vec<LogInfo>;
459
460 fn pool(&self) -> OperationVc<EvaluatePool> {
461 get_evaluate_pool(
462 self.entries,
463 self.cwd.clone(),
464 self.env,
465 self.node_backend,
466 self.chunking_context,
467 self.module_graph,
468 self.additional_invalidation,
469 should_debug("webpack_loader"),
470 EnvVarTracking::Untracked,
474 )
475 }
476
477 fn args(&self) -> &[ResolvedVc<serde_json::Value>] {
478 &self.args
479 }
480
481 fn cwd(&self) -> Vc<turbo_tasks_fs::FileSystemPath> {
482 self.cwd.clone().cell()
483 }
484
485 fn keep_alive(&self) -> bool {
486 true
487 }
488
489 async fn emit_error(&self, error: StructuredError, pool: &EvaluatePool) -> Result<()> {
490 EvaluationIssue {
491 error,
492 source: IssueSource::from_source_only(self.context_source_for_issue),
493 assets_for_source_mapping: pool.assets_for_source_mapping,
494 assets_root: pool.assets_root.clone(),
495 root_path: self.chunking_context.root_path().owned().await?,
496 }
497 .resolved_cell()
498 .emit();
499 Ok(())
500 }
501
502 async fn info(
503 &self,
504 state: &mut Self::State,
505 data: Self::InfoMessage,
506 pool: &EvaluatePool,
507 ) -> Result<()> {
508 match data {
509 InfoMessage::Dependencies {
510 env_variables,
511 file_paths,
512 directories,
513 build_file_paths,
514 } => {
515 if turbo_tasks::turbo_tasks().is_tracking_dependencies() {
518 let env_subscriptions = env_variables
526 .iter()
527 .map(|e| self.env.read(e.clone()))
528 .try_join();
529 let file_subscriptions = file_paths
530 .iter()
531 .map(|p| async move { self.cwd.join(p)?.read().await })
532 .try_join();
533 let directory_subscriptions = directories
534 .iter()
535 .map(|(dir, glob)| async move {
536 self.cwd
537 .join(dir)?
538 .track_glob(Glob::new(glob.clone(), GlobOptions::default()), false)
539 .await
540 })
541 .try_join();
542 try_join!(
543 env_subscriptions,
544 file_subscriptions,
545 directory_subscriptions
546 )?;
547
548 for build_path in build_file_paths {
549 let build_path = self.cwd.join(&build_path)?;
550 BuildDependencyIssue {
551 source: IssueSource::from_source_only(self.context_source_for_issue),
552 path: build_path,
553 }
554 .resolved_cell()
555 .emit();
556 }
557 }
558 }
559 InfoMessage::EmittedError { error, severity } => {
560 EvaluateEmittedErrorIssue {
561 source: IssueSource::from_source_only(self.context_source_for_issue),
562 error,
563 severity,
564 assets_for_source_mapping: pool.assets_for_source_mapping,
565 assets_root: pool.assets_root.clone(),
566 project_dir: self.chunking_context.root_path().owned().await?,
567 }
568 .resolved_cell()
569 .emit();
570 }
571 InfoMessage::Log { logs } => {
572 state.extend(logs);
573 }
574 }
575 Ok(())
576 }
577
578 async fn request(
579 &self,
580 _state: &mut Self::State,
581 data: Self::RequestMessage,
582 _pool: &EvaluatePool,
583 ) -> Result<Self::ResponseMessage> {
584 match data {
585 RequestMessage::Resolve {
586 options: webpack_options,
587 lookup_path,
588 request,
589 } => {
590 let Some(resolve_options_context) = self.resolve_options_context else {
591 bail!("Resolve options are not available in this context");
592 };
593 let lookup_path = self.cwd.join(&lookup_path)?;
594 let request = Request::parse(Pattern::Constant(request));
595 let options = resolve_options(lookup_path.clone(), *resolve_options_context);
596
597 let options = apply_webpack_resolve_options(options, webpack_options);
598
599 let resolved = resolve(
600 lookup_path.clone(),
601 ReferenceType::Undefined,
602 request,
603 options,
604 );
605
606 if let Some(source) = *resolved.first_source().await? {
607 if let Some(path) = self
608 .cwd
609 .get_relative_path_to(&*source.ident().path().await?)
610 {
611 Ok(ResponseMessage::Resolve { path })
612 } else {
613 turbobail!(
614 "Resolving {request} in {lookup_path} ends up on a different \
615 filesystem"
616 );
617 }
618 } else {
619 turbobail!("Unable to resolve {request} in {lookup_path}");
620 }
621 }
622 RequestMessage::TrackFileRead { file } => {
623 let _ = &*self.cwd.join(&file)?.read().await?;
626 Ok(ResponseMessage::TrackFileRead {})
627 }
628 }
629 }
630
631 async fn finish(&self, state: Self::State, pool: &EvaluatePool) -> Result<()> {
632 let has_errors = state.iter().any(|log| log.log_type == LogType::Error);
633 let has_warnings = state.iter().any(|log| log.log_type == LogType::Warn);
634 if has_errors || has_warnings {
635 let logs = state
636 .into_iter()
637 .filter(|log| {
638 matches!(
639 log.log_type,
640 LogType::Error
641 | LogType::Warn
642 | LogType::Info
643 | LogType::Log
644 | LogType::Clear,
645 )
646 })
647 .collect();
648
649 EvaluateErrorLoggingIssue {
650 source: IssueSource::from_source_only(self.context_source_for_issue),
651 logging: logs,
652 severity: if has_errors {
653 IssueSeverity::Error
654 } else {
655 IssueSeverity::Warning
656 },
657 assets_for_source_mapping: pool.assets_for_source_mapping,
658 assets_root: pool.assets_root.clone(),
659 project_dir: self.chunking_context.root_path().owned().await?,
660 }
661 .resolved_cell()
662 .emit();
663 }
664 Ok(())
665 }
666}
667
668#[turbo_tasks::function]
669async fn apply_webpack_resolve_options(
670 resolve_options: Vc<ResolveOptions>,
671 webpack_resolve_options: WebpackResolveOptions,
672) -> Result<Vc<ResolveOptions>> {
673 let mut resolve_options = resolve_options.owned().await?;
674 if let Some(alias_fields) = webpack_resolve_options.alias_fields {
675 let mut old = resolve_options
676 .in_package
677 .extract_if(0.., |field| {
678 matches!(field, ResolveInPackage::AliasField(..))
679 })
680 .collect::<Vec<_>>();
681 for field in alias_fields {
682 if &*field == "..." {
683 resolve_options.in_package.extend(take(&mut old));
684 } else {
685 resolve_options
686 .in_package
687 .push(ResolveInPackage::AliasField(field));
688 }
689 }
690 }
691 if let Some(condition_names) = webpack_resolve_options.condition_names {
692 for conditions in get_condition_maps(&mut resolve_options) {
693 let mut old = take(conditions);
694 for name in &condition_names {
695 if name == "..." {
696 conditions.extend(take(&mut old));
697 } else {
698 conditions.insert(name.clone(), ConditionValue::Set);
699 }
700 }
701 }
702 }
703 if webpack_resolve_options.no_package_json {
704 resolve_options.into_package.retain(|item| {
705 !matches!(
706 item,
707 ResolveIntoPackage::ExportsField { .. } | ResolveIntoPackage::MainField { .. }
708 )
709 });
710 }
711 if let Some(mut extensions) = webpack_resolve_options.extensions {
712 if let Some(pos) = extensions.iter().position(|ext| ext == "...") {
713 extensions.splice(pos..=pos, take(&mut resolve_options.extensions));
714 }
715 resolve_options.extensions = extensions;
716 }
717 if let Some(main_fields) = webpack_resolve_options.main_fields {
718 let mut old = resolve_options
719 .into_package
720 .extract_if(0.., |field| {
721 matches!(field, ResolveIntoPackage::MainField { .. })
722 })
723 .collect::<Vec<_>>();
724 for field in main_fields {
725 if &*field == "..." {
726 resolve_options.into_package.extend(take(&mut old));
727 } else {
728 resolve_options
729 .into_package
730 .push(ResolveIntoPackage::MainField { field });
731 }
732 }
733 }
734 if webpack_resolve_options.no_exports_field {
735 resolve_options
736 .into_package
737 .retain(|field| !matches!(field, ResolveIntoPackage::ExportsField { .. }));
738 }
739 if let Some(main_files) = webpack_resolve_options.main_files {
740 resolve_options.default_files = main_files;
741 }
742 if webpack_resolve_options.no_modules {
743 resolve_options.modules.clear();
744 }
745 if webpack_resolve_options.prefer_relative {
746 resolve_options.prefer_relative = true;
747 }
748 Ok(resolve_options.cell())
749}
750
751#[turbo_tasks::value(shared)]
753pub struct BuildDependencyIssue {
754 pub path: FileSystemPath,
755 pub source: IssueSource,
756}
757
758#[turbo_tasks::value_impl]
759impl Issue for BuildDependencyIssue {
760 fn severity(&self) -> IssueSeverity {
761 IssueSeverity::Warning
762 }
763
764 #[turbo_tasks::function]
765 fn title(&self) -> Vc<StyledString> {
766 StyledString::Text(rcstr!("Build dependencies are not yet supported")).cell()
767 }
768
769 #[turbo_tasks::function]
770 fn stage(&self) -> Vc<IssueStage> {
771 IssueStage::Unsupported.cell()
772 }
773
774 #[turbo_tasks::function]
775 fn file_path(&self) -> Vc<FileSystemPath> {
776 self.source.file_path()
777 }
778
779 #[turbo_tasks::function]
780 async fn description(&self) -> Result<Vc<OptionStyledString>> {
781 Ok(Vc::cell(Some(
782 StyledString::Line(vec![
783 StyledString::Text(rcstr!("The file at ")),
784 StyledString::Code(self.path.to_string().into()),
785 StyledString::Text(
786 " is a build dependency, which is not yet implemented.
787 Changing this file or any dependency will not be recognized and might require restarting the \
788 server"
789 .into(),
790 ),
791 ])
792 .resolved_cell(),
793 )))
794 }
795
796 #[turbo_tasks::function]
797 fn source(&self) -> Vc<OptionIssueSource> {
798 Vc::cell(Some(self.source))
799 }
800}
801
802#[turbo_tasks::value(shared)]
803pub struct EvaluateEmittedErrorIssue {
804 pub source: IssueSource,
805 pub severity: IssueSeverity,
806 pub error: StructuredError,
807 pub assets_for_source_mapping: ResolvedVc<AssetsForSourceMapping>,
808 pub assets_root: FileSystemPath,
809 pub project_dir: FileSystemPath,
810}
811
812#[turbo_tasks::value_impl]
813impl Issue for EvaluateEmittedErrorIssue {
814 #[turbo_tasks::function]
815 fn file_path(&self) -> Vc<FileSystemPath> {
816 self.source.file_path()
817 }
818
819 #[turbo_tasks::function]
820 fn stage(&self) -> Vc<IssueStage> {
821 IssueStage::Transform.cell()
822 }
823
824 fn severity(&self) -> IssueSeverity {
825 self.severity
826 }
827
828 #[turbo_tasks::function]
829 fn title(&self) -> Vc<StyledString> {
830 StyledString::Text(rcstr!("Issue while running loader")).cell()
831 }
832
833 #[turbo_tasks::function]
834 async fn description(&self) -> Result<Vc<OptionStyledString>> {
835 Ok(Vc::cell(Some(
836 StyledString::Text(
837 self.error
838 .print(
839 *self.assets_for_source_mapping,
840 self.assets_root.clone(),
841 self.project_dir.clone(),
842 FormattingMode::Plain,
843 )
844 .await?
845 .into(),
846 )
847 .resolved_cell(),
848 )))
849 }
850
851 #[turbo_tasks::function]
852 fn source(&self) -> Vc<OptionIssueSource> {
853 Vc::cell(Some(self.source))
854 }
855}
856
857#[turbo_tasks::value(shared)]
858pub struct EvaluateErrorLoggingIssue {
859 pub source: IssueSource,
860 pub severity: IssueSeverity,
861 #[turbo_tasks(trace_ignore)]
862 pub logging: Vec<LogInfo>,
863 pub assets_for_source_mapping: ResolvedVc<AssetsForSourceMapping>,
864 pub assets_root: FileSystemPath,
865 pub project_dir: FileSystemPath,
866}
867
868#[turbo_tasks::value_impl]
869impl Issue for EvaluateErrorLoggingIssue {
870 #[turbo_tasks::function]
871 fn file_path(&self) -> Vc<FileSystemPath> {
872 self.source.file_path()
873 }
874
875 #[turbo_tasks::function]
876 fn stage(&self) -> Vc<IssueStage> {
877 IssueStage::Transform.cell()
878 }
879
880 fn severity(&self) -> IssueSeverity {
881 self.severity
882 }
883
884 #[turbo_tasks::function]
885 fn title(&self) -> Vc<StyledString> {
886 StyledString::Text(rcstr!("Error logging while running loader")).cell()
887 }
888
889 #[turbo_tasks::function]
890 fn description(&self) -> Vc<OptionStyledString> {
891 fn fmt_args(prefix: String, args: &[JsonValue]) -> String {
892 let mut iter = args.iter();
893 let Some(first) = iter.next() else {
894 return "".to_string();
895 };
896 let mut result = prefix;
897 if let JsonValue::String(s) = first {
898 result.push_str(s);
899 } else {
900 result.push_str(&first.to_string());
901 }
902 for arg in iter {
903 result.push(' ');
904 result.push_str(&arg.to_string());
905 }
906 result
907 }
908 let lines = self
909 .logging
910 .iter()
911 .map(|log| match log.log_type {
912 LogType::Error => {
913 StyledString::Strong(fmt_args("<e> ".to_string(), &log.args).into())
914 }
915 LogType::Warn => StyledString::Text(fmt_args("<w> ".to_string(), &log.args).into()),
916 LogType::Info => StyledString::Text(fmt_args("<i> ".to_string(), &log.args).into()),
917 LogType::Log => StyledString::Text(fmt_args("<l> ".to_string(), &log.args).into()),
918 LogType::Clear => StyledString::Strong(rcstr!("---")),
919 _ => {
920 unimplemented!("{:?} is not implemented", log.log_type)
921 }
922 })
923 .collect::<Vec<_>>();
924 Vc::cell(Some(StyledString::Stack(lines).resolved_cell()))
925 }
926
927 #[turbo_tasks::function]
928 fn source(&self) -> Vc<OptionIssueSource> {
929 Vc::cell(Some(self.source))
930 }
931}