2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2020-09-05 20:34:02 -04:00
2023-05-25 14:27:45 -04:00
use once_cell ::sync ::OnceCell ;
2022-11-25 19:04:30 -05:00
use super ::DiskCache ;
2022-07-12 18:58:39 -04:00
2023-03-16 12:22:24 -04:00
use std ::env ;
2018-07-26 17:54:22 -04:00
use std ::path ::PathBuf ;
2019-06-24 12:04:06 -04:00
2023-05-25 14:27:45 -04:00
/// Lazily creates the deno dir which might be useful in scenarios
/// where functionality wants to continue if the DENO_DIR can't be created.
pub struct DenoDirProvider {
maybe_custom_root : Option < PathBuf > ,
deno_dir : OnceCell < std ::io ::Result < DenoDir > > ,
}
impl DenoDirProvider {
pub fn new ( maybe_custom_root : Option < PathBuf > ) -> Self {
Self {
maybe_custom_root ,
deno_dir : Default ::default ( ) ,
}
}
pub fn get_or_create ( & self ) -> Result < & DenoDir , std ::io ::Error > {
self
. deno_dir
. get_or_init ( | | DenoDir ::new ( self . maybe_custom_root . clone ( ) ) )
. as_ref ( )
. map_err ( | err | std ::io ::Error ::new ( err . kind ( ) , err . to_string ( ) ) )
}
}
2019-07-17 18:15:30 -04:00
/// `DenoDir` serves as coordinator for multiple `DiskCache`s containing them
/// in single directory that can be controlled with `$DENO_DIR` env variable.
2019-04-01 21:46:40 -04:00
#[ derive(Clone) ]
2018-07-26 17:54:22 -04:00
pub struct DenoDir {
2020-06-20 23:49:27 -04:00
/// Example: /Users/rld/.deno/
2022-11-25 19:04:30 -05:00
/// Note: This is not exposed in order to encourage using re-usable methods.
root : PathBuf ,
2019-07-31 07:58:41 -04:00
/// Used by TsCompiler to cache compiler output.
pub gen_cache : DiskCache ,
2018-07-26 17:54:22 -04:00
}
impl DenoDir {
2020-05-15 10:32:52 -04:00
pub fn new ( maybe_custom_root : Option < PathBuf > ) -> std ::io ::Result < Self > {
2023-05-25 14:27:45 -04:00
let maybe_custom_root =
maybe_custom_root . or_else ( | | env ::var ( " DENO_DIR " ) . map ( String ::into ) . ok ( ) ) ;
2020-05-15 10:32:52 -04:00
let root : PathBuf = if let Some ( root ) = maybe_custom_root {
2022-02-03 08:08:17 -05:00
root
2020-07-13 12:24:54 -04:00
} else if let Some ( cache_dir ) = dirs ::cache_dir ( ) {
// We use the OS cache dir because all files deno writes are cache files
// Once that changes we need to start using different roots if DENO_DIR
// is not set, and keep a single one if it is.
cache_dir . join ( " deno " )
} else if let Some ( home_dir ) = dirs ::home_dir ( ) {
// fallback path
home_dir . join ( " .deno " )
2020-05-15 10:32:52 -04:00
} else {
2020-07-13 12:24:54 -04:00
panic! ( " Could not set the Deno root directory " )
2020-05-15 10:32:52 -04:00
} ;
2022-02-03 08:08:17 -05:00
let root = if root . is_absolute ( ) {
root
} else {
std ::env ::current_dir ( ) ? . join ( root )
} ;
2020-05-15 10:32:52 -04:00
assert! ( root . is_absolute ( ) ) ;
2019-07-31 07:58:41 -04:00
let gen_path = root . join ( " gen " ) ;
2019-04-29 10:58:31 -04:00
2018-11-05 01:21:21 -05:00
let deno_dir = Self {
2018-08-14 16:50:53 -04:00
root ,
2019-07-31 07:58:41 -04:00
gen_cache : DiskCache ::new ( & gen_path ) ,
2018-08-14 16:50:53 -04:00
} ;
2019-01-17 23:39:06 -05:00
2018-07-26 17:54:22 -04:00
Ok ( deno_dir )
}
2022-04-19 22:14:00 -04:00
2022-11-25 19:04:30 -05:00
/// The root directory of the DENO_DIR for display purposes only.
pub fn root_path_for_display ( & self ) -> std ::path ::Display {
self . root . display ( )
}
2022-04-19 22:14:00 -04:00
/// Path for the incremental cache used for formatting.
pub fn fmt_incremental_cache_db_file_path ( & self ) -> PathBuf {
// bump this version name to invalidate the entire cache
self . root . join ( " fmt_incremental_cache_v1 " )
}
/// Path for the incremental cache used for linting.
pub fn lint_incremental_cache_db_file_path ( & self ) -> PathBuf {
// bump this version name to invalidate the entire cache
self . root . join ( " lint_incremental_cache_v1 " )
}
2022-07-12 18:58:39 -04:00
2022-08-22 12:14:59 -04:00
/// Path for caching swc dependency analysis.
pub fn dep_analysis_db_file_path ( & self ) -> PathBuf {
// bump this version name to invalidate the entire cache
self . root . join ( " dep_analysis_cache_v1 " )
}
2022-10-01 06:15:56 -04:00
/// Path for caching node analysis.
pub fn node_analysis_db_file_path ( & self ) -> PathBuf {
// bump this version name to invalidate the entire cache
self . root . join ( " node_analysis_cache_v1 " )
}
2022-07-19 11:58:18 -04:00
/// Path for the cache used for type checking.
2022-07-12 18:58:39 -04:00
pub fn type_checking_cache_db_file_path ( & self ) -> PathBuf {
// bump this version name to invalidate the entire cache
self . root . join ( " check_cache_v1 " )
}
2022-11-25 19:04:30 -05:00
/// Path to the registries cache, used for the lps.
pub fn registries_folder_path ( & self ) -> PathBuf {
self . root . join ( " registries " )
}
/// Path to the dependencies cache folder.
pub fn deps_folder_path ( & self ) -> PathBuf {
self . root . join ( " deps " )
}
/// Path to the origin data cache folder.
pub fn origin_data_folder_path ( & self ) -> PathBuf {
// TODO(@crowlKats): change to origin_data for 2.0
self . root . join ( " location_data " )
}
/// File used for the upgrade checker.
pub fn upgrade_check_file_path ( & self ) -> PathBuf {
self . root . join ( " latest.txt " )
}
/// Folder used for the npm cache.
pub fn npm_folder_path ( & self ) -> PathBuf {
self . root . join ( " npm " )
}
/// Path used for the REPL history file.
2023-03-16 12:22:24 -04:00
/// Can be overridden or disabled by setting `DENO_REPL_HISTORY` environment variable.
pub fn repl_history_file_path ( & self ) -> Option < PathBuf > {
if let Some ( deno_repl_history ) = env ::var_os ( " DENO_REPL_HISTORY " ) {
if deno_repl_history . is_empty ( ) {
None
} else {
Some ( PathBuf ::from ( deno_repl_history ) )
}
} else {
Some ( self . root . join ( " deno_history.txt " ) )
}
2022-11-25 19:04:30 -05:00
}
/// Folder path used for downloading new versions of deno.
pub fn dl_folder_path ( & self ) -> PathBuf {
self . root . join ( " dl " )
}
2019-07-17 18:15:30 -04:00
}
2020-06-20 23:49:27 -04:00
/// To avoid the poorly managed dirs crate
#[ cfg(not(windows)) ]
mod dirs {
use std ::path ::PathBuf ;
pub fn cache_dir ( ) -> Option < PathBuf > {
if cfg! ( target_os = " macos " ) {
home_dir ( ) . map ( | h | h . join ( " Library/Caches " ) )
} else {
std ::env ::var_os ( " XDG_CACHE_HOME " )
. map ( PathBuf ::from )
. or_else ( | | home_dir ( ) . map ( | h | h . join ( " .cache " ) ) )
}
}
pub fn home_dir ( ) -> Option < PathBuf > {
std ::env ::var_os ( " HOME " )
. and_then ( | h | if h . is_empty ( ) { None } else { Some ( h ) } )
2022-06-25 18:13:24 -04:00
. or_else ( | | {
// TODO(bartlomieju):
#[ allow(clippy::undocumented_unsafe_blocks) ]
unsafe {
fallback ( )
}
} )
2020-06-20 23:49:27 -04:00
. map ( PathBuf ::from )
}
2020-07-13 12:24:54 -04:00
// This piece of code is taken from the deprecated home_dir() function in Rust's standard library: https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/os.rs#L579
// The same code is used by the dirs crate
unsafe fn fallback ( ) -> Option < std ::ffi ::OsString > {
let amt = match libc ::sysconf ( libc ::_SC_GETPW_R_SIZE_MAX ) {
2020-11-27 14:47:35 -05:00
n if n < 0 = > 512_ usize ,
2020-07-13 12:24:54 -04:00
n = > n as usize ,
} ;
let mut buf = Vec ::with_capacity ( amt ) ;
let mut passwd : libc ::passwd = std ::mem ::zeroed ( ) ;
let mut result = std ::ptr ::null_mut ( ) ;
match libc ::getpwuid_r (
libc ::getuid ( ) ,
& mut passwd ,
buf . as_mut_ptr ( ) ,
buf . capacity ( ) ,
& mut result ,
) {
0 if ! result . is_null ( ) = > {
let ptr = passwd . pw_dir as * const _ ;
let bytes = std ::ffi ::CStr ::from_ptr ( ptr ) . to_bytes ( ) . to_vec ( ) ;
Some ( std ::os ::unix ::ffi ::OsStringExt ::from_vec ( bytes ) )
}
_ = > None ,
}
}
2020-06-20 23:49:27 -04:00
}
/// To avoid the poorly managed dirs crate
// Copied from
// https://github.com/dirs-dev/dirs-sys-rs/blob/ec7cee0b3e8685573d847f0a0f60aae3d9e07fa2/src/lib.rs#L140-L164
// MIT license. Copyright (c) 2018-2019 dirs-rs contributors
#[ cfg(windows) ]
mod dirs {
use std ::ffi ::OsString ;
use std ::os ::windows ::ffi ::OsStringExt ;
use std ::path ::PathBuf ;
use winapi ::shared ::winerror ;
2023-01-14 23:18:58 -05:00
use winapi ::um ::combaseapi ;
use winapi ::um ::knownfolders ;
use winapi ::um ::shlobj ;
use winapi ::um ::shtypes ;
use winapi ::um ::winbase ;
use winapi ::um ::winnt ;
2020-06-20 23:49:27 -04:00
fn known_folder ( folder_id : shtypes ::REFKNOWNFOLDERID ) -> Option < PathBuf > {
2022-07-15 12:30:25 -04:00
// SAFETY: winapi calls
2020-06-20 23:49:27 -04:00
unsafe {
let mut path_ptr : winnt ::PWSTR = std ::ptr ::null_mut ( ) ;
let result = shlobj ::SHGetKnownFolderPath (
folder_id ,
0 ,
std ::ptr ::null_mut ( ) ,
& mut path_ptr ,
) ;
if result = = winerror ::S_OK {
let len = winbase ::lstrlenW ( path_ptr ) as usize ;
let path = std ::slice ::from_raw_parts ( path_ptr , len ) ;
let ostr : OsString = OsStringExt ::from_wide ( path ) ;
combaseapi ::CoTaskMemFree ( path_ptr as * mut winapi ::ctypes ::c_void ) ;
Some ( PathBuf ::from ( ostr ) )
} else {
None
}
}
}
pub fn cache_dir ( ) -> Option < PathBuf > {
known_folder ( & knownfolders ::FOLDERID_LocalAppData )
}
pub fn home_dir ( ) -> Option < PathBuf > {
known_folder ( & knownfolders ::FOLDERID_Profile )
}
}