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: ResolvedVc<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 async fn new(
29        root: ResolvedVc<FileSystemPath>,
30        env: ResolvedVc<Box<dyn ProcessEnv>>,
31    ) -> Result<Vc<Self>> {
32        Ok(ProcessEnvAsset { root, env }.cell())
33    }
34}
35
36#[turbo_tasks::value_impl]
37impl Source for ProcessEnvAsset {
38    #[turbo_tasks::function]
39    fn ident(&self) -> Vc<AssetIdent> {
40        AssetIdent::from_path(self.root.join(".env.js".into()))
41    }
42}
43
44#[turbo_tasks::value_impl]
45impl Asset for ProcessEnvAsset {
46    #[turbo_tasks::function]
47    async fn content(&self) -> Result<Vc<AssetContent>> {
48        let env = self.env.read_all().await?;
49
50        // TODO: In SSR, we use the native process.env, which can only contain string
51        // values. We need to inject literal values (to emulate webpack's
52        // DefinePlugin), so create a new regular object out of the old env.
53        let mut code = RopeBuilder::default();
54        code += "const env = process.env = {...process.env};\n\n";
55
56        for (name, val) in &*env {
57            // It's assumed the env has passed through an EmbeddableProcessEnv, so the value
58            // is ready to be directly embedded. Values _after_ an embeddable
59            // env can be used to inject live code into the output.
60            // TODO this is not completely correct as env vars need to ignore casing
61            // So `process.env.path === process.env.PATH === process.env.PaTh`
62            writeln!(code, "env[{}] = {};", StringifyJs(name), val)?;
63        }
64
65        Ok(AssetContent::file(File::from(code.build()).into()))
66    }
67}