Skip to main content

turbopack_env/
asset.rs

1use std::io::Write;
2
3use anyhow::Result;
4use turbo_rcstr::{RcStr, rcstr};
5use turbo_tasks::{ResolvedVc, Vc};
6use turbo_tasks_env::ProcessEnv;
7use turbo_tasks_fs::{File, FileContent, FileSystemPath, rope::RopeBuilder};
8use turbopack_core::{
9    asset::{Asset, AssetContent},
10    ident::AssetIdent,
11    source::Source,
12};
13use turbopack_ecmascript::utils::StringifyJs;
14
15/// The `process.env` asset, responsible for initializing the env (shared by all
16/// chunks) during app startup.
17#[turbo_tasks::value]
18pub struct ProcessEnvAsset {
19    /// The root path which we can construct our env asset path.
20    root: FileSystemPath,
21
22    /// A HashMap filled with the env key/values.
23    env: ResolvedVc<Box<dyn ProcessEnv>>,
24}
25
26#[turbo_tasks::value_impl]
27impl ProcessEnvAsset {
28    #[turbo_tasks::function]
29    pub fn new(root: FileSystemPath, env: ResolvedVc<Box<dyn ProcessEnv>>) -> Result<Vc<Self>> {
30        Ok(ProcessEnvAsset { root, env }.cell())
31    }
32}
33
34#[turbo_tasks::value_impl]
35impl Source for ProcessEnvAsset {
36    #[turbo_tasks::function]
37    fn ident(&self) -> Result<Vc<AssetIdent>> {
38        Ok(AssetIdent::from_path(self.root.join(".env.js")?))
39    }
40
41    #[turbo_tasks::function]
42    fn description(&self) -> Vc<RcStr> {
43        Vc::cell(rcstr!("process environment"))
44    }
45}
46
47#[turbo_tasks::value_impl]
48impl Asset for ProcessEnvAsset {
49    #[turbo_tasks::function]
50    async fn content(&self) -> Result<Vc<AssetContent>> {
51        let env = self.env.read_all().await?;
52
53        // TODO: In SSR, we use the native process.env, which can only contain string
54        // values. We need to inject literal values (to emulate webpack's
55        // DefinePlugin), so create a new regular object out of the old env.
56        let mut code = RopeBuilder::default();
57        code += "const env = process.env = {...process.env};\n\n";
58
59        for (name, val) in &*env {
60            // It's assumed the env has passed through an EmbeddableProcessEnv, so the value
61            // is ready to be directly embedded. Values _after_ an embeddable
62            // env can be used to inject live code into the output.
63            // TODO this is not completely correct as env vars need to ignore casing
64            // So `process.env.path === process.env.PATH === process.env.PaTh`
65            writeln!(code, "env[{}] = {};", StringifyJs(name), val)?;
66        }
67
68        Ok(AssetContent::file(
69            FileContent::Content(File::from(code.build())).cell(),
70        ))
71    }
72}