Skip to main content

turbopack_dev_server/source/
wrapping_source.rs

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