Skip to main content

turbopack_browser/ecmascript/evaluate/
runtime.rs

1use anyhow::Result;
2use turbo_rcstr::rcstr;
3use turbo_tasks::{ResolvedVc, ValueToString, Vc};
4use turbo_tasks_fs::{File, FileContent, FileSystem, FileSystemPath};
5use turbopack_core::{
6    asset::{Asset, AssetContent},
7    chunk::{ChunkingContext, MinifyType},
8    code_builder::{Code, CodeBuilder},
9    ident::AssetIdent,
10    output::{OutputAsset, OutputAssetsReference, OutputAssetsWithReferenced},
11    source_map::{GenerateSourceMap, SourceMapAsset},
12};
13use turbopack_ecmascript::minify::minify;
14use turbopack_ecmascript_runtime::RuntimeType;
15
16use crate::BrowserChunkingContext;
17
18/// An Ecmascript chunk that contains the Turbopack browser runtime code.
19///
20/// This is emitted once per [`BrowserChunkingContext`] (deduplicated by
21/// turbo-tasks on its inputs) and shared by every entrypoint, instead of being
22/// inlined into each `EcmascriptBrowserEvaluateChunk`. It
23/// mirrors the Node.js `turbopack_nodejs`'s `EcmascriptBuildNodeRuntimeChunk`.
24#[turbo_tasks::value(shared)]
25#[derive(ValueToString)]
26#[value_to_string("Ecmascript Browser Runtime Chunk")]
27pub struct EcmascriptBrowserRuntimeChunk {
28    chunking_context: ResolvedVc<BrowserChunkingContext>,
29    include_async_module_runtime: bool,
30}
31
32#[turbo_tasks::value_impl]
33impl EcmascriptBrowserRuntimeChunk {
34    #[turbo_tasks::function]
35    pub fn new(
36        chunking_context: ResolvedVc<BrowserChunkingContext>,
37        include_async_module_runtime: bool,
38    ) -> Vc<Self> {
39        EcmascriptBrowserRuntimeChunk {
40            chunking_context,
41            include_async_module_runtime,
42        }
43        .cell()
44    }
45
46    #[turbo_tasks::function]
47    pub(crate) async fn code(self: Vc<Self>) -> Result<Vc<Code>> {
48        let this = self.await?;
49        let chunking_context = this.chunking_context;
50        let environment = chunking_context.environment();
51
52        let output_root_to_root_path = chunking_context.output_root_to_root_path().owned().await?;
53        let source_maps = *chunking_context
54            .reference_chunk_source_maps(Vc::upcast(self))
55            .await?;
56        let asset_context = turbopack::get_runtime_asset_context(environment);
57        let runtime_type = *chunking_context.runtime_type().await?;
58
59        let mut code = CodeBuilder::new(source_maps, *chunking_context.debug_ids_enabled().await?);
60
61        match runtime_type {
62            RuntimeType::Production | RuntimeType::Development => {
63                let runtime_code = turbopack_ecmascript_runtime::get_browser_runtime_code(
64                    asset_context,
65                    chunking_context.chunk_base_path(),
66                    chunking_context.asset_suffix(),
67                    runtime_type,
68                    output_root_to_root_path,
69                    source_maps,
70                    chunking_context.chunk_loading_global(),
71                    chunking_context.cross_origin(),
72                    chunking_context.chunk_load_retry(),
73                    this.include_async_module_runtime,
74                    chunking_context.chunk_loading(),
75                    *chunking_context.generate_component_chunks().await?,
76                );
77                code.push_code(&*runtime_code.await?);
78            }
79            #[cfg(feature = "test")]
80            RuntimeType::Dummy => {
81                let runtime_code = turbopack_ecmascript_runtime::get_dummy_runtime_code();
82                code.push_code(&runtime_code);
83            }
84        }
85
86        let mut code = code.build();
87
88        if let MinifyType::Minify { mangle } = *chunking_context.minify_type().await? {
89            code = minify(code, source_maps, mangle)?;
90        }
91
92        Ok(code.cell())
93    }
94
95    #[turbo_tasks::function]
96    async fn ident_for_path(self: Vc<Self>) -> Result<Vc<AssetIdent>> {
97        Ok(AssetIdent::from_path(
98            turbopack_ecmascript_runtime::embed_fs()
99                .root()
100                .await?
101                .join("runtime.js")?,
102        )
103        .into_vc())
104    }
105
106    #[turbo_tasks::function]
107    async fn source_map(self: Vc<Self>) -> Result<Vc<SourceMapAsset>> {
108        let this = self.await?;
109        Ok(SourceMapAsset::new(
110            Vc::upcast(*this.chunking_context),
111            self.ident_for_path(),
112            Vc::upcast(self),
113        ))
114    }
115}
116
117#[turbo_tasks::value_impl]
118impl OutputAssetsReference for EcmascriptBrowserRuntimeChunk {
119    #[turbo_tasks::function]
120    async fn references(self: Vc<Self>) -> Result<Vc<OutputAssetsWithReferenced>> {
121        let this = self.await?;
122        let mut references = vec![];
123
124        if *this
125            .chunking_context
126            .reference_chunk_source_maps(Vc::upcast(self))
127            .await?
128        {
129            references.push(ResolvedVc::upcast(self.source_map().to_resolved().await?))
130        }
131
132        Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(
133            references,
134        )))
135    }
136}
137
138#[turbo_tasks::value_impl]
139impl OutputAsset for EcmascriptBrowserRuntimeChunk {
140    #[turbo_tasks::function]
141    async fn path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
142        let this = self.await?;
143        let ident = self.ident_for_path();
144        Ok(this.chunking_context.chunk_path(
145            Some(Vc::upcast(self)),
146            ident,
147            Some(rcstr!("turbopack")),
148            rcstr!(".js"),
149        ))
150    }
151}
152
153#[turbo_tasks::value_impl]
154impl Asset for EcmascriptBrowserRuntimeChunk {
155    #[turbo_tasks::function]
156    async fn content(self: Vc<Self>) -> Result<Vc<AssetContent>> {
157        Ok(AssetContent::file(
158            FileContent::Content(File::from(
159                self.code()
160                    .to_rope_with_magic_comments(|| self.source_map())
161                    .await?,
162            ))
163            .cell(),
164        ))
165    }
166}
167
168#[turbo_tasks::value_impl]
169impl GenerateSourceMap for EcmascriptBrowserRuntimeChunk {
170    #[turbo_tasks::function]
171    fn generate_source_map(self: Vc<Self>) -> Vc<FileContent> {
172        self.code().generate_source_map()
173    }
174}