Skip to main content

turbopack_browser/ecmascript/evaluate/
single_entry_chunk.rs

1use anyhow::Result;
2use turbo_tasks::{ResolvedVc, ValueToString, Vc};
3use turbo_tasks_fs::{File, FileContent, FileSystemPath};
4use turbopack_core::{
5    asset::{Asset, AssetContent},
6    chunk::{Chunk, ChunkingContext, EvaluatableAssets},
7    code_builder::{Code, CodeBuilder},
8    module_graph::ModuleGraph,
9    output::{
10        OutputAsset, OutputAssets, OutputAssetsReference, OutputAssetsReferences,
11        OutputAssetsWithReferenced,
12    },
13    source_map::{GenerateSourceMap, SourceMapAsset},
14};
15use turbopack_ecmascript::chunk::EcmascriptChunk;
16
17use crate::{
18    BrowserChunkingContext,
19    ecmascript::{chunk::EcmascriptBrowserChunk, evaluate::chunk::EcmascriptBrowserEvaluateChunk},
20};
21
22/// A self-contained chunk that inlines the entire module together with
23/// the Turbopack browser runtime and the evaluation of the entry modules.
24///
25/// Unlike the regular evaluate chunk, this emits everything into one file at a
26/// fixed path and never loads additional chunks at runtime.
27#[turbo_tasks::value(shared)]
28#[derive(ValueToString)]
29#[value_to_string("Ecmascript Browser Single Entry Chunk")]
30pub struct EcmascriptBrowserSingleEntryChunk {
31    chunking_context: ResolvedVc<BrowserChunkingContext>,
32    path: FileSystemPath,
33    chunk: ResolvedVc<EcmascriptChunk>,
34    evaluatable_assets: ResolvedVc<EvaluatableAssets>,
35    referenced_output_assets: ResolvedVc<OutputAssets>,
36    references: ResolvedVc<OutputAssetsReferences>,
37    module_graph: ResolvedVc<ModuleGraph>,
38}
39
40#[turbo_tasks::value_impl]
41impl EcmascriptBrowserSingleEntryChunk {
42    #[turbo_tasks::function]
43    pub fn new(
44        chunking_context: ResolvedVc<BrowserChunkingContext>,
45        path: FileSystemPath,
46        chunk: ResolvedVc<EcmascriptChunk>,
47        evaluatable_assets: ResolvedVc<EvaluatableAssets>,
48        referenced_output_assets: ResolvedVc<OutputAssets>,
49        references: ResolvedVc<OutputAssetsReferences>,
50        module_graph: ResolvedVc<ModuleGraph>,
51    ) -> Vc<Self> {
52        EcmascriptBrowserSingleEntryChunk {
53            chunking_context,
54            path,
55            chunk,
56            evaluatable_assets,
57            referenced_output_assets,
58            references,
59            module_graph,
60        }
61        .cell()
62    }
63
64    #[turbo_tasks::function]
65    async fn code(self: Vc<Self>) -> Result<Vc<Code>> {
66        let this = self.await?;
67
68        let source_maps = *this
69            .chunking_context
70            .reference_chunk_source_maps(Vc::upcast(self))
71            .await?;
72        let mut code = CodeBuilder::new(
73            source_maps,
74            *this.chunking_context.debug_ids_enabled().await?,
75        );
76
77        let module_chunk = EcmascriptBrowserChunk::new(*this.chunking_context, *this.chunk);
78        code.push_code(&*module_chunk.own_content().code().await?);
79
80        let evaluate_chunk = EcmascriptBrowserEvaluateChunk::new(
81            *this.chunking_context,
82            this.chunk.ident(),
83            OutputAssets::empty(),
84            *this.evaluatable_assets,
85            *this.module_graph,
86        );
87        code.push_code(&*evaluate_chunk.code().await?);
88
89        // Append the shared runtime chunk; without `shared_runtime` it's already inlined above.
90        if *this.chunking_context.shared_runtime().await? {
91            let runtime_chunk = this
92                .chunking_context
93                .generate_runtime_chunk(*this.module_graph)
94                .await?;
95            code.push_code(&*runtime_chunk.code().await?);
96        }
97
98        Ok(Code::cell(code.build()))
99    }
100
101    #[turbo_tasks::function]
102    async fn source_map(self: Vc<Self>) -> Result<Vc<SourceMapAsset>> {
103        let this = self.await?;
104        Ok(SourceMapAsset::new_fixed(
105            this.path.clone(),
106            Vc::upcast(self),
107        ))
108    }
109}
110
111#[turbo_tasks::value_impl]
112impl OutputAssetsReference for EcmascriptBrowserSingleEntryChunk {
113    #[turbo_tasks::function]
114    async fn references(self: Vc<Self>) -> Result<Vc<OutputAssetsWithReferenced>> {
115        let this = self.await?;
116        let mut assets = Vec::new();
117
118        if *this
119            .chunking_context
120            .reference_chunk_source_maps(Vc::upcast(self))
121            .await?
122        {
123            assets.push(ResolvedVc::upcast(self.source_map().to_resolved().await?));
124        }
125
126        Ok(OutputAssetsWithReferenced {
127            assets: ResolvedVc::cell(assets),
128            referenced_assets: this.referenced_output_assets,
129            references: this.references,
130        }
131        .cell())
132    }
133}
134
135#[turbo_tasks::value_impl]
136impl OutputAsset for EcmascriptBrowserSingleEntryChunk {
137    #[turbo_tasks::function]
138    fn path(&self) -> Vc<FileSystemPath> {
139        self.path.clone().cell()
140    }
141}
142
143#[turbo_tasks::value_impl]
144impl Asset for EcmascriptBrowserSingleEntryChunk {
145    #[turbo_tasks::function]
146    async fn content(self: Vc<Self>) -> Result<Vc<AssetContent>> {
147        Ok(AssetContent::file(
148            FileContent::Content(File::from(
149                self.code()
150                    .to_rope_with_magic_comments(|| self.source_map())
151                    .await?,
152            ))
153            .cell(),
154        ))
155    }
156}
157
158#[turbo_tasks::value_impl]
159impl GenerateSourceMap for EcmascriptBrowserSingleEntryChunk {
160    #[turbo_tasks::function]
161    fn generate_source_map(self: Vc<Self>) -> Vc<FileContent> {
162        self.code().generate_source_map()
163    }
164}