turbopack_env/
asset.rs

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