turbopack_dev_server/source/
wrapping_source.rs

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