turbopack_dev_server/source/
wrapping_source.rs

1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{OperationVc, ResolvedVc, TryJoinIterExt, Value, Vc};
4
5use super::{
6    ContentSourceContent, ContentSourceData, ContentSourceDataVary, GetContentSourceContent,
7    GetContentSourceContents, Rewrite, RewriteType,
8};
9
10/// A ContentSourceProcessor handles the final processing of an eventual
11/// [ContentSourceContent].
12///
13/// Used in conjunction with [WrappedGetContentSourceContent], this allows a
14/// [ContentSource] implementation to easily register a final process step over
15/// some inner ContentSource's fully resolved [ContentSourceResult] and
16/// [ContentSourceContent].
17#[turbo_tasks::value_trait]
18pub trait ContentSourceProcessor {
19    fn process(self: Vc<Self>, content: Vc<ContentSourceContent>) -> Vc<ContentSourceContent>;
20}
21
22/// A WrappedGetContentSourceContent simply wraps the get_content of a
23/// [ContentSourceResult], allowing us to process whatever
24/// [ContentSourceContent] it would have returned.
25
26#[turbo_tasks::value]
27pub struct WrappedGetContentSourceContent {
28    inner: ResolvedVc<Box<dyn GetContentSourceContent>>,
29    processor: ResolvedVc<Box<dyn ContentSourceProcessor>>,
30}
31
32#[turbo_tasks::value_impl]
33impl WrappedGetContentSourceContent {
34    #[turbo_tasks::function]
35    pub fn new(
36        inner: ResolvedVc<Box<dyn GetContentSourceContent>>,
37        processor: ResolvedVc<Box<dyn ContentSourceProcessor>>,
38    ) -> Vc<Self> {
39        WrappedGetContentSourceContent { inner, processor }.cell()
40    }
41}
42
43#[turbo_tasks::function(operation)]
44async fn wrap_sources_operation(
45    sources: OperationVc<GetContentSourceContents>,
46    processor: ResolvedVc<Box<dyn ContentSourceProcessor>>,
47) -> Result<Vc<GetContentSourceContents>> {
48    Ok(Vc::cell(
49        sources
50            .connect()
51            .await?
52            .iter()
53            .map(|s| {
54                Vc::upcast::<Box<dyn GetContentSourceContent>>(WrappedGetContentSourceContent::new(
55                    **s, *processor,
56                ))
57            })
58            .map(|v| async move { v.to_resolved().await })
59            .try_join()
60            .await?,
61    ))
62}
63
64#[turbo_tasks::value_impl]
65impl GetContentSourceContent for WrappedGetContentSourceContent {
66    #[turbo_tasks::function]
67    fn vary(&self) -> Vc<ContentSourceDataVary> {
68        self.inner.vary()
69    }
70
71    #[turbo_tasks::function]
72    async fn get(
73        &self,
74        path: RcStr,
75        data: Value<ContentSourceData>,
76    ) -> Result<Vc<ContentSourceContent>> {
77        let res = self.inner.get(path, data);
78        if let ContentSourceContent::Rewrite(rewrite) = &*res.await? {
79            let rewrite = rewrite.await?;
80            return Ok(ContentSourceContent::Rewrite(
81                Rewrite {
82                    ty: match &rewrite.ty {
83                        RewriteType::Location { .. } | RewriteType::ContentSource { .. } => todo!(
84                            "Rewrites for WrappedGetContentSourceContent are not implemented yet"
85                        ),
86                        RewriteType::Sources { sources } => RewriteType::Sources {
87                            sources: wrap_sources_operation(*sources, self.processor),
88                        },
89                    },
90                    response_headers: rewrite.response_headers,
91                    request_headers: rewrite.request_headers,
92                }
93                .resolved_cell(),
94            )
95            .cell());
96        }
97        Ok(self.processor.process(res))
98    }
99}