diff --git a/Cargo.lock b/Cargo.lock index 37dca4b4a0ebf..79facd203f3ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2288,6 +2288,7 @@ dependencies = [ "tokio", "turbo-tasks", "turbo-tasks-build", + "turbo-tasks-env", "turbo-tasks-fs", "turbopack", "turbopack-core", @@ -4941,6 +4942,21 @@ dependencies = [ "turbo-tasks-macros-shared", ] +[[package]] +name = "turbo-tasks-env" +version = "0.1.0" +dependencies = [ + "anyhow", + "dotenvy", + "indexmap", + "serde", + "serde_json", + "tokio", + "turbo-tasks", + "turbo-tasks-build", + "turbo-tasks-fs", +] + [[package]] name = "turbo-tasks-fs" version = "0.1.0" @@ -5036,6 +5052,7 @@ dependencies = [ "turbo-malloc", "turbo-tasks", "turbo-tasks-build", + "turbo-tasks-env", "turbo-tasks-fs", "turbo-tasks-memory", "turbopack-core", @@ -5177,6 +5194,7 @@ dependencies = [ "serde", "turbo-tasks", "turbo-tasks-build", + "turbo-tasks-env", "turbo-tasks-fs", "turbopack-core", "turbopack-ecmascript", diff --git a/Cargo.toml b/Cargo.toml index 135292b7c1eb4..ffe2ad750fbbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "crates/swc-ast-explorer", "crates/turbo-malloc", "crates/turbo-tasks-build", + "crates/turbo-tasks-env", "crates/turbo-tasks-fs", "crates/turbo-tasks-macros", "crates/turbo-tasks-memory", diff --git a/crates/next-core/Cargo.toml b/crates/next-core/Cargo.toml index fad26ae38014b..89a951c125942 100644 --- a/crates/next-core/Cargo.toml +++ b/crates/next-core/Cargo.toml @@ -16,6 +16,7 @@ serde = "1.0.136" serde_json = "1.0.85" tokio = { version = "1.11.0", features = ["full"] } turbo-tasks = { path = "../turbo-tasks" } +turbo-tasks-env = { path = "../turbo-tasks-env" } turbo-tasks-fs = { path = "../turbo-tasks-fs" } turbopack = { path = "../turbopack" } turbopack-core = { path = "../turbopack-core" } diff --git a/crates/next-core/src/env.rs b/crates/next-core/src/env.rs index e39e9626754f9..3e9b4fed1d74a 100644 --- a/crates/next-core/src/env.rs +++ b/crates/next-core/src/env.rs @@ -1,12 +1,15 @@ use anyhow::Result; +use turbo_tasks_env::{CommandLineProcessEnvVc, FilterProcessEnvVc, ProcessEnvVc}; use turbo_tasks_fs::FileSystemPathVc; -use turbopack_env::ProcessEnvVc; +use turbopack_env::TryDotenvProcessEnvVc; /// Loads a series of dotenv files according to the precedence rules set by /// https://nextjs.org/docs/basic-features/environment-variables#environment-variable-load-order #[turbo_tasks::function] pub async fn load_env(project_path: FileSystemPathVc) -> Result { - let node_env = std::env::var("NODE_ENV").unwrap_or_else(|_| "development".into()); + let env = CommandLineProcessEnvVc::new().as_process_env(); + let node_env = env.read("NODE_ENV").await?; + let node_env = node_env.as_deref().unwrap_or("development"); let files = [ Some(format!(".env.{node_env}.local")), @@ -21,14 +24,14 @@ pub async fn load_env(project_path: FileSystemPathVc) -> Result { .into_iter() .flatten(); - let env = files.fold(ProcessEnvVc::from_command_line(), |prior, f| { + let env = files.fold(env, |prior, f| { let path = project_path.join(&f); - ProcessEnvVc::from_dotenv_file(path, Some(prior)) + TryDotenvProcessEnvVc::new(prior, path).as_process_env() }); Ok(env) } pub fn filter_for_client(env: ProcessEnvVc) -> ProcessEnvVc { - ProcessEnvVc::filter(env, "NEXT_PUBLIC_".to_string()) + FilterProcessEnvVc::new(env, "NEXT_PUBLIC_".to_string()).into() } diff --git a/crates/next-core/src/next_client/context.rs b/crates/next-core/src/next_client/context.rs index 7b0a8de62d8ce..bf2034bfb8d82 100644 --- a/crates/next-core/src/next_client/context.rs +++ b/crates/next-core/src/next_client/context.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use anyhow::Result; use turbo_tasks::Value; +use turbo_tasks_env::ProcessEnvVc; use turbo_tasks_fs::FileSystemPathVc; use turbopack::{ module_options::module_options_context::{ModuleOptionsContext, ModuleOptionsContextVc}, @@ -17,7 +18,7 @@ use turbopack_core::{ }; use turbopack_dev_server::html_runtime_asset::HtmlRuntimeAssetVc; use turbopack_ecmascript::chunk::EcmascriptChunkPlaceablesVc; -use turbopack_env::{ProcessEnvAssetVc, ProcessEnvVc}; +use turbopack_env::ProcessEnvAssetVc; use crate::{ env::filter_for_client, diff --git a/crates/next-core/src/server_rendered_source.rs b/crates/next-core/src/server_rendered_source.rs index a3f02a174efcb..c034a11a5477d 100644 --- a/crates/next-core/src/server_rendered_source.rs +++ b/crates/next-core/src/server_rendered_source.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use anyhow::Result; use serde_json::json; use turbo_tasks::{primitives::JsonValueVc, Value}; +use turbo_tasks_env::ProcessEnvVc; use turbo_tasks_fs::{DirectoryContent, DirectoryEntry, FileSystemEntryType, FileSystemPathVc}; use turbopack::{ module_options::ModuleOptionsContext, resolve_options_context::ResolveOptionsContext, @@ -21,7 +22,7 @@ use turbopack_dev_server::source::{ ContentSourceVc, NoContentSourceVc, }; use turbopack_ecmascript::chunk::EcmascriptChunkPlaceablesVc; -use turbopack_env::{ProcessEnvAssetVc, ProcessEnvVc}; +use turbopack_env::ProcessEnvAssetVc; use crate::{ next_client::{ diff --git a/crates/next-core/src/web_entry_source.rs b/crates/next-core/src/web_entry_source.rs index e05beaaa79f07..236936b0cd80d 100644 --- a/crates/next-core/src/web_entry_source.rs +++ b/crates/next-core/src/web_entry_source.rs @@ -2,6 +2,7 @@ use std::future::IntoFuture; use anyhow::{anyhow, Result}; use futures::{prelude::*, stream}; +use turbo_tasks_env::ProcessEnvVc; use turbo_tasks_fs::FileSystemPathVc; use turbopack::ecmascript::EcmascriptModuleAssetVc; use turbopack_core::{ @@ -12,7 +13,6 @@ use turbopack_dev_server::{ html::DevHtmlAsset, source::{asset_graph::AssetGraphContentSourceVc, ContentSourceVc}, }; -use turbopack_env::ProcessEnvVc; use crate::next_client::context::{ get_client_asset_context, get_client_chunking_context, get_resolved_client_runtime_entries, diff --git a/crates/turbo-tasks-env/Cargo.toml b/crates/turbo-tasks-env/Cargo.toml new file mode 100644 index 0000000000000..7cafc61b38ca2 --- /dev/null +++ b/crates/turbo-tasks-env/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "turbo-tasks-env" +version = "0.1.0" +edition = "2021" + +[lib] +bench = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.47" +dotenvy = "0.15.5" +indexmap = "1.8.0" +serde = "1.0.136" +serde_json = "1.0.85" +tokio = "1.11.0" +turbo-tasks = { path = "../turbo-tasks" } +turbo-tasks-fs = { path = "../turbo-tasks-fs" } + +[build-dependencies] +turbo-tasks-build = { path = "../turbo-tasks-build" } diff --git a/crates/turbo-tasks-env/build.rs b/crates/turbo-tasks-env/build.rs new file mode 100644 index 0000000000000..1673efed59cce --- /dev/null +++ b/crates/turbo-tasks-env/build.rs @@ -0,0 +1,5 @@ +use turbo_tasks_build::generate_register; + +fn main() { + generate_register(); +} diff --git a/crates/turbo-tasks-env/src/command_line.rs b/crates/turbo-tasks-env/src/command_line.rs new file mode 100644 index 0000000000000..0eec95c40621f --- /dev/null +++ b/crates/turbo-tasks-env/src/command_line.rs @@ -0,0 +1,31 @@ +use std::env; + +use indexmap::IndexMap; + +use crate::{EnvMapVc, ProcessEnv, ProcessEnvVc, GLOBAL_ENV_LOCK}; + +/// Load the environment variables defined via command line. +#[turbo_tasks::value] +pub struct CommandLineProcessEnv; + +#[turbo_tasks::value_impl] +impl CommandLineProcessEnvVc { + #[turbo_tasks::function] + pub fn new() -> Self { + CommandLineProcessEnv.cell() + } +} + +/// Clones the current env vars into a IndexMap. +fn env_snapshot() -> IndexMap { + let _lock = GLOBAL_ENV_LOCK.lock().unwrap(); + env::vars().collect::>() +} + +#[turbo_tasks::value_impl] +impl ProcessEnv for CommandLineProcessEnv { + #[turbo_tasks::function] + fn read_all(&self) -> EnvMapVc { + EnvMapVc::cell(env_snapshot()) + } +} diff --git a/crates/turbo-tasks-env/src/dotenv.rs b/crates/turbo-tasks-env/src/dotenv.rs new file mode 100644 index 0000000000000..54718a02911ea --- /dev/null +++ b/crates/turbo-tasks-env/src/dotenv.rs @@ -0,0 +1,89 @@ +use std::env; + +use anyhow::{anyhow, Context, Result}; +use indexmap::IndexMap; +use turbo_tasks::ValueToString; +use turbo_tasks_fs::{FileContent, FileSystemPathVc}; + +use crate::{EnvMapVc, ProcessEnv, ProcessEnvVc, GLOBAL_ENV_LOCK}; + +/// Load the environment variables defined via a dotenv file, with an +/// optional prior state that we can lookup already defined variables +/// from. +#[turbo_tasks::value] +pub struct DotenvProcessEnv { + prior: Option, + path: FileSystemPathVc, +} + +#[turbo_tasks::value_impl] +impl DotenvProcessEnvVc { + #[turbo_tasks::function] + pub fn new(prior: Option, path: FileSystemPathVc) -> Self { + DotenvProcessEnv { prior, path }.cell() + } +} + +#[turbo_tasks::value_impl] +impl ProcessEnv for DotenvProcessEnv { + #[turbo_tasks::function] + async fn read_all(&self) -> Result { + let prior = if let Some(p) = self.prior { + Some(p.read_all().await?) + } else { + None + }; + let empty = IndexMap::new(); + let prior = prior.as_deref().unwrap_or(&empty); + + let file = self.path.read().await?; + if let FileContent::Content(f) = &*file { + let res; + let vars; + { + let _lock = GLOBAL_ENV_LOCK.lock().unwrap(); + + // Unfortunately, dotenvy only looks up variable references from the global env. + // So we must mutate while we process. Afterwards, we can restore the initial + // state. + let initial = env::vars().collect(); + + restore_env(&initial, prior); + + // from_read will load parse and evalute the Read, and set variables + // into the global env. If a later dotenv defines an already defined + // var, it'll be ignored. + res = dotenvy::from_read(f.content()); + + vars = env::vars().collect(); + restore_env(&vars, &initial); + } + + if res.is_err() { + res.context(anyhow!( + "unable to read {} for env vars", + self.path.to_string().await? + ))?; + } + + Ok(EnvMapVc::cell(vars)) + } else { + Ok(EnvMapVc::cell(prior.clone())) + } + } +} + +/// Restores the global env variables to mirror `to`. +fn restore_env(from: &IndexMap, to: &IndexMap) { + for key in from.keys() { + if !to.contains_key(key) { + env::remove_var(key); + } + } + for (key, value) in to { + match from.get(key) { + Some(v) if v == value => {} + _ => env::set_var(key, value), + } + } +} diff --git a/crates/turbo-tasks-env/src/filter.rs b/crates/turbo-tasks-env/src/filter.rs new file mode 100644 index 0000000000000..ac5f69400e452 --- /dev/null +++ b/crates/turbo-tasks-env/src/filter.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use indexmap::IndexMap; +use turbo_tasks::primitives::OptionStringVc; + +use crate::{EnvMapVc, ProcessEnv, ProcessEnvVc}; + +#[turbo_tasks::value] +pub struct FilterProcessEnv { + prior: ProcessEnvVc, + filter: String, +} + +#[turbo_tasks::value_impl] +impl FilterProcessEnvVc { + #[turbo_tasks::function] + pub fn new(prior: ProcessEnvVc, filter: String) -> Self { + FilterProcessEnv { prior, filter }.cell() + } +} + +#[turbo_tasks::value_impl] +impl ProcessEnv for FilterProcessEnv { + #[turbo_tasks::function] + async fn read_all(&self) -> Result { + let prior = self.prior.read_all().await?; + let mut filtered = IndexMap::new(); + for (key, value) in &*prior { + if key.starts_with(&self.filter) { + filtered.insert(key.clone(), value.clone()); + } + } + Ok(EnvMapVc::cell(filtered)) + } + + #[turbo_tasks::function] + fn read(&self, name: &str) -> OptionStringVc { + if name.starts_with(&self.filter) { + self.prior.read(name) + } else { + OptionStringVc::cell(None) + } + } +} diff --git a/crates/turbo-tasks-env/src/lib.rs b/crates/turbo-tasks-env/src/lib.rs new file mode 100644 index 0000000000000..9b247a8325f2a --- /dev/null +++ b/crates/turbo-tasks-env/src/lib.rs @@ -0,0 +1,51 @@ +#![feature(min_specialization)] + +mod command_line; +mod dotenv; +mod filter; + +use std::{env, sync::Mutex}; + +use anyhow::Result; +use indexmap::IndexMap; +use turbo_tasks::primitives::OptionStringVc; + +pub use self::{ + command_line::CommandLineProcessEnvVc, dotenv::DotenvProcessEnvVc, filter::FilterProcessEnvVc, +}; + +#[turbo_tasks::value(transparent)] +pub struct EnvMap(#[turbo_tasks(trace_ignore)] IndexMap); + +#[turbo_tasks::value_impl] +impl EnvMapVc { + #[turbo_tasks::function] + pub fn empty() -> Self { + EnvMap(IndexMap::new()).cell() + } +} + +#[turbo_tasks::value_trait] +pub trait ProcessEnv { + // TODO SECURITY: From security perspective it's not good that we read *all* env + // vars into the cache. This might store secrects into the persistent cache + // which we want to avoid. + // Instead we should use only `read_prefix` to read all env vars with a specific + // prefix. + /// Reads all env variables into a Map + fn read_all(&self) -> EnvMapVc; + + /// Reads a single env variable + async fn read(&self, name: &str) -> Result { + Ok(OptionStringVc::cell( + self.read_all().await?.get(name).cloned(), + )) + } +} + +pub static GLOBAL_ENV_LOCK: Mutex<()> = Mutex::new(()); + +pub fn register() { + turbo_tasks::register(); + include!(concat!(env!("OUT_DIR"), "/register.rs")); +} diff --git a/crates/turbo-tasks-fs/src/lib.rs b/crates/turbo-tasks-fs/src/lib.rs index 919ac7f570442..b26fe219b4df7 100644 --- a/crates/turbo-tasks-fs/src/lib.rs +++ b/crates/turbo-tasks-fs/src/lib.rs @@ -904,7 +904,7 @@ impl FileSystemPathVc { } #[turbo_tasks::function] - //It is important that get_type uses read_dir and not stat/metadata. + // It is important that get_type uses read_dir and not stat/metadata. // - `get_type` is called very very often during resolving and stat would // make it 1 syscall per call, whereas read_dir would make it 1 syscall per // directory. diff --git a/crates/turbo-tasks/src/primitives.rs b/crates/turbo-tasks/src/primitives.rs index 3f004e66a5cc3..1ca629dae9846 100644 --- a/crates/turbo-tasks/src/primitives.rs +++ b/crates/turbo-tasks/src/primitives.rs @@ -15,6 +15,9 @@ impl StringVc { } } +#[turbo_tasks::value(transparent)] +pub struct OptionString(Option); + #[turbo_tasks::value(transparent)] pub struct Strings(Vec); diff --git a/crates/turbopack-env/Cargo.toml b/crates/turbopack-env/Cargo.toml index a1ee762bc65fd..793019e48b610 100644 --- a/crates/turbopack-env/Cargo.toml +++ b/crates/turbopack-env/Cargo.toml @@ -15,6 +15,7 @@ dotenvy = "0.15.5" indexmap = "1.8.0" serde = "1.0.136" turbo-tasks = { path = "../turbo-tasks" } +turbo-tasks-env = { path = "../turbo-tasks-env" } turbo-tasks-fs = { path = "../turbo-tasks-fs" } turbopack-core = { path = "../turbopack-core" } turbopack-ecmascript = { path = "../turbopack-ecmascript" } diff --git a/crates/turbopack-env/src/asset.rs b/crates/turbopack-env/src/asset.rs index f16317161058c..7609c0ea8d549 100644 --- a/crates/turbopack-env/src/asset.rs +++ b/crates/turbopack-env/src/asset.rs @@ -2,6 +2,7 @@ use std::fmt::Write as _; use anyhow::Result; use turbo_tasks::{primitives::StringVc, ValueToString, ValueToStringVc}; +use turbo_tasks_env::ProcessEnvVc; use turbo_tasks_fs::FileSystemPathVc; use turbopack_core::{ asset::{Asset, AssetContentVc, AssetVc}, @@ -17,8 +18,6 @@ use turbopack_ecmascript::{ utils::stringify_str, }; -use crate::ProcessEnvVc; - /// The `process.env` asset, responsible for initializing the env (shared by all /// chunks) during app startup. #[turbo_tasks::value] @@ -117,7 +116,7 @@ impl EcmascriptChunkItem for ProcessEnvChunkItem { #[turbo_tasks::function] async fn content(&self) -> Result { let asset = self.inner.await?; - let env = asset.env.read().await?; + let env = asset.env.read_all().await?; let mut code = "const env = process.env;\n\n".to_string(); for (name, val) in &*env { diff --git a/crates/turbopack-env/src/lib.rs b/crates/turbopack-env/src/lib.rs index e80273cb4b074..ce0e8b75ce75f 100644 --- a/crates/turbopack-env/src/lib.rs +++ b/crates/turbopack-env/src/lib.rs @@ -14,158 +14,16 @@ mod asset; mod issue; +mod try_env; -use std::env; - -use anyhow::Result; pub use asset::{ProcessEnvAsset, ProcessEnvAssetVc}; -use indexmap::IndexMap; pub use issue::{ProcessEnvIssue, ProcessEnvIssueVc}; -use turbo_tasks::primitives::StringVc; -use turbo_tasks_fs::{FileContent, FileSystemPathVc}; - -type EnvMap = IndexMap; - -#[turbo_tasks::value(transparent, serialization = "none")] -pub struct EnvRead(#[turbo_tasks(trace_ignore)] EnvMap); - -#[turbo_tasks::value_impl] -impl EnvReadVc { - #[turbo_tasks::function] - pub fn empty() -> Self { - EnvRead(IndexMap::new()).cell() - } -} - -#[turbo_tasks::value] -pub enum ProcessEnv { - /// The environment variables defined via the command line. This is - /// immutable once the rust binary process has loaded. - CommandLine(), - - Filter { - prior: ProcessEnvVc, - filter: String, - }, - - /// The environment variables defined in a dotenv file. Environment - /// variables that are defined prior may be passed, and in the case of - /// confilct, the prior variable will have priority. This dotenv file - /// can reference any variable defined prior. - DotenvFile { - path: FileSystemPathVc, - prior: Option, - }, -} - -/// Clones the current env vars into a IndexMap. -fn env_snapshot() -> IndexMap { - env::vars().collect::>() -} - -#[turbo_tasks::value_impl] -impl ProcessEnvVc { - /// Load the environment variables defined via command line. - #[turbo_tasks::function] - pub fn from_command_line() -> Self { - ProcessEnv::CommandLine().cell() - } - - /// Load the environment variables defined via a dotenv file, with an - /// optional prior state that we can lookup already defined variables - /// from. - #[turbo_tasks::function] - pub fn from_dotenv_file(path: FileSystemPathVc, prior: Option) -> Self { - ProcessEnv::DotenvFile { path, prior }.cell() - } - - #[turbo_tasks::function] - pub fn filter(prior: ProcessEnvVc, filter: String) -> Self { - ProcessEnv::Filter { prior, filter }.cell() - } - - /// Computes the full ProcessEnv chain into a usable IndexMap. - #[turbo_tasks::function] - pub async fn read(self) -> Result { - let this = self.await?; - let snapshot = match &*this { - ProcessEnv::CommandLine() => env_snapshot(), - ProcessEnv::Filter { prior, filter } => filter_env(prior, filter).await?, - ProcessEnv::DotenvFile { path, prior } => load_dotenv(path, prior).await?, - }; - Ok(EnvRead(snapshot).cell()) - } -} - -async fn filter_env(prior: &ProcessEnvVc, filter: &String) -> Result { - let prior = prior.read().await?; - let mut filtered = IndexMap::new(); - for (key, value) in &*prior { - if key.starts_with(filter) { - filtered.insert(key.clone(), value.clone()); - } - } - Ok(filtered) -} - -async fn load_dotenv(path: &FileSystemPathVc, prior: &Option) -> Result { - let prior = match prior { - None => EnvReadVc::empty(), - Some(prior) => prior.read(), - } - .await?; - - let file = path.read().await?; - if let FileContent::Content(f) = &*file { - // Unfortunately, dotenvy only looks up variable references from the global env. - // So we must mutate while we process. Afterwards, we can restore the initial - // state. - let initial = env_snapshot(); - let p = &*prior; - restore_env(&initial, p); - - // from_read will load parse and evalute the Read, and set variables - // into the global env. If a later dotenv defines an already defined - // var, it'll be ignored. - let res = dotenvy::from_read(f.content()); - - let vars = env_snapshot(); - restore_env(&vars, &initial); - - if let Err(err) = res { - ProcessEnvIssue { - path: *path, - description: StringVc::cell(err.to_string()), - } - .cell() - .as_issue() - .emit(); - } - - Ok(vars) - } else { - Ok(prior.clone()) - } -} - -/// Restores the global env variables to mirror `to`. -fn restore_env(from: &EnvMap, to: &EnvMap) { - for key in from.keys() { - if !to.contains_key(key) { - env::remove_var(key); - } - } - for (key, value) in to { - match from.get(key) { - Some(v) if v == value => {} - _ => env::set_var(key, value), - } - } -} +pub use try_env::TryDotenvProcessEnvVc; pub fn register() { turbo_tasks::register(); turbo_tasks_fs::register(); + turbo_tasks_env::register(); turbopack_core::register(); turbopack_ecmascript::register(); include!(concat!(env!("OUT_DIR"), "/register.rs")); diff --git a/crates/turbopack-env/src/try_env.rs b/crates/turbopack-env/src/try_env.rs new file mode 100644 index 0000000000000..0641a609a0b13 --- /dev/null +++ b/crates/turbopack-env/src/try_env.rs @@ -0,0 +1,62 @@ +use std::future::IntoFuture; + +use anyhow::Result; +use turbo_tasks::primitives::{OptionStringVc, StringVc}; +use turbo_tasks_env::{DotenvProcessEnvVc, EnvMapVc, ProcessEnv, ProcessEnvVc}; +use turbo_tasks_fs::FileSystemPathVc; + +use crate::ProcessEnvIssue; + +#[turbo_tasks::value] +pub struct TryDotenvProcessEnv { + prior: ProcessEnvVc, + path: FileSystemPathVc, +} + +impl TryDotenvProcessEnv { + async fn with_issue>>( + &self, + op: impl Fn(ProcessEnvVc) -> V, + ) -> Result { + let r = op(DotenvProcessEnvVc::new(Some(self.prior), self.path).as_process_env()); + match r.await { + Ok(_) => Ok(r), + Err(e) => { + let r = op(self.prior); + // If the prior process env also reports an error we don't want to report our + // issue + r.await?; + + ProcessEnvIssue { + path: self.path, + description: StringVc::cell(e.to_string()), + } + .cell() + .as_issue() + .emit(); + Ok(r) + } + } + } +} + +#[turbo_tasks::value_impl] +impl TryDotenvProcessEnvVc { + #[turbo_tasks::function] + pub fn new(prior: ProcessEnvVc, path: FileSystemPathVc) -> Self { + TryDotenvProcessEnv { prior, path }.cell() + } +} + +#[turbo_tasks::value_impl] +impl ProcessEnv for TryDotenvProcessEnv { + #[turbo_tasks::function] + async fn read_all(&self) -> Result { + self.with_issue(|e| e.read_all()).await + } + + #[turbo_tasks::function] + async fn read(&self, name: &str) -> Result { + self.with_issue(|e| e.read(name)).await + } +} diff --git a/crates/turbopack/Cargo.toml b/crates/turbopack/Cargo.toml index 537378ff55ac4..e359a59096295 100644 --- a/crates/turbopack/Cargo.toml +++ b/crates/turbopack/Cargo.toml @@ -23,6 +23,7 @@ serde_json = "1.0.85" serde_regex = "1.1.0" tokio = "1.11.0" turbo-tasks = { path = "../turbo-tasks" } +turbo-tasks-env = { path = "../turbo-tasks-env" } turbo-tasks-fs = { path = "../turbo-tasks-fs" } turbopack-core = { path = "../turbopack-core" } turbopack-css = { path = "../turbopack-css" } diff --git a/crates/turbopack/tests/snapshot.rs b/crates/turbopack/tests/snapshot.rs index b5eb7e61663b9..72fb3bbcb33e6 100644 --- a/crates/turbopack/tests/snapshot.rs +++ b/crates/turbopack/tests/snapshot.rs @@ -13,6 +13,7 @@ use helpers::print_changeset; use lazy_static::lazy_static; use test_generator::test_resources; use turbo_tasks::{NothingVc, TryJoinIterExt, TurboTasks, Value}; +use turbo_tasks_env::DotenvProcessEnvVc; use turbo_tasks_fs::{ util::sys_to_unix, DirectoryContent, DirectoryEntry, DiskFileSystemVc, File, FileContent, FileSystemEntryType, FileSystemPathVc, FileSystemVc, @@ -33,7 +34,7 @@ use turbopack_core::{ reference::all_referenced_assets, source_asset::SourceAssetVc, }; -use turbopack_env::{ProcessEnvAssetVc, ProcessEnvVc}; +use turbopack_env::ProcessEnvAssetVc; lazy_static! { // Allows for interactive manual debugging of a test case in a browser with: @@ -282,8 +283,8 @@ async fn maybe_load_env( return Ok(None); } - let env = ProcessEnvVc::from_dotenv_file(dotenv_path, None); - let asset = ProcessEnvAssetVc::new(dotenv_path, env); + let env = DotenvProcessEnvVc::new(None, dotenv_path); + let asset = ProcessEnvAssetVc::new(dotenv_path, env.into()); Ok(Some(EcmascriptChunkPlaceablesVc::cell(vec![ asset.as_ecmascript_chunk_placeable() ])))