2024-01-01 14:58:21 -05:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2022-09-13 11:59:01 -04:00
2022-09-22 11:17:02 -04:00
//! Code for global npm cache resolution.
2024-09-24 15:23:57 -04:00
use std ::borrow ::Cow ;
2022-09-13 11:59:01 -04:00
use std ::path ::Path ;
2022-09-22 11:17:02 -04:00
use std ::path ::PathBuf ;
2023-04-14 16:22:33 -04:00
use std ::sync ::Arc ;
2022-09-13 11:59:01 -04:00
2024-09-24 15:23:57 -04:00
use crate ::colors ;
2023-02-22 14:15:25 -05:00
use async_trait ::async_trait ;
2022-09-13 11:59:01 -04:00
use deno_ast ::ModuleSpecifier ;
use deno_core ::error ::AnyError ;
use deno_core ::url ::Url ;
2023-04-06 18:46:44 -04:00
use deno_npm ::NpmPackageCacheFolderId ;
use deno_npm ::NpmPackageId ;
2024-09-24 15:23:57 -04:00
use deno_npm ::NpmResolutionPackage ;
2023-05-17 17:38:50 -04:00
use deno_npm ::NpmSystemInfo ;
2023-05-10 20:06:59 -04:00
use deno_runtime ::deno_fs ::FileSystem ;
2023-01-10 08:35:44 -05:00
use deno_runtime ::deno_node ::NodePermissions ;
2024-07-25 19:08:14 -04:00
use node_resolver ::errors ::PackageFolderResolveError ;
use node_resolver ::errors ::PackageNotFoundError ;
use node_resolver ::errors ::ReferrerNotFoundError ;
2022-09-13 11:59:01 -04:00
2024-09-24 15:23:57 -04:00
use crate ::args ::LifecycleScriptsConfig ;
use crate ::cache ::FastInsecureHasher ;
2023-10-02 17:53:55 -04:00
use super ::super ::cache ::NpmCache ;
2024-06-02 21:39:13 -04:00
use super ::super ::cache ::TarballCache ;
2023-09-30 12:06:38 -04:00
use super ::super ::resolution ::NpmResolution ;
use super ::common ::cache_packages ;
2024-09-24 15:23:57 -04:00
use super ::common ::lifecycle_scripts ::LifecycleScriptsStrategy ;
2023-02-22 14:15:25 -05:00
use super ::common ::NpmPackageFsResolver ;
2023-06-09 14:41:18 -04:00
use super ::common ::RegistryReadPermissionChecker ;
2022-09-13 11:59:01 -04:00
2022-09-22 11:17:02 -04:00
/// Resolves packages from the global npm cache.
2023-04-14 16:22:33 -04:00
#[ derive(Debug) ]
2022-09-13 11:59:01 -04:00
pub struct GlobalNpmPackageResolver {
2023-04-14 16:22:33 -04:00
cache : Arc < NpmCache > ,
2024-06-02 21:39:13 -04:00
tarball_cache : Arc < TarballCache > ,
2023-04-14 16:22:33 -04:00
resolution : Arc < NpmResolution > ,
2023-05-17 17:38:50 -04:00
system_info : NpmSystemInfo ,
2023-06-09 14:41:18 -04:00
registry_read_permission_checker : RegistryReadPermissionChecker ,
2024-09-24 15:23:57 -04:00
lifecycle_scripts : LifecycleScriptsConfig ,
2022-09-13 11:59:01 -04:00
}
impl GlobalNpmPackageResolver {
2022-09-28 13:04:16 -04:00
pub fn new (
2023-04-14 16:22:33 -04:00
cache : Arc < NpmCache > ,
2024-06-02 21:39:13 -04:00
fs : Arc < dyn FileSystem > ,
tarball_cache : Arc < TarballCache > ,
2023-04-14 16:22:33 -04:00
resolution : Arc < NpmResolution > ,
2023-05-17 17:38:50 -04:00
system_info : NpmSystemInfo ,
2024-09-24 15:23:57 -04:00
lifecycle_scripts : LifecycleScriptsConfig ,
2022-09-28 13:04:16 -04:00
) -> Self {
2022-09-13 11:59:01 -04:00
Self {
2023-06-09 14:41:18 -04:00
registry_read_permission_checker : RegistryReadPermissionChecker ::new (
fs ,
2024-05-23 17:26:23 -04:00
cache . root_folder ( ) ,
2023-06-09 14:41:18 -04:00
) ,
2024-06-02 21:39:13 -04:00
cache ,
tarball_cache ,
resolution ,
system_info ,
2024-09-24 15:23:57 -04:00
lifecycle_scripts ,
2022-09-13 11:59:01 -04:00
}
}
}
2024-06-06 18:37:41 -04:00
#[ async_trait(?Send) ]
2023-02-22 14:15:25 -05:00
impl NpmPackageFsResolver for GlobalNpmPackageResolver {
2023-02-23 10:58:10 -05:00
fn root_dir_url ( & self ) -> & Url {
self . cache . root_dir_url ( )
}
2024-09-30 09:33:32 -04:00
fn node_modules_path ( & self ) -> Option < & Path > {
2023-03-12 23:32:59 -04:00
None
}
2024-07-09 12:15:03 -04:00
fn maybe_package_folder ( & self , id : & NpmPackageId ) -> Option < PathBuf > {
2023-04-14 16:22:33 -04:00
let folder_id = self
. resolution
2024-07-09 12:15:03 -04:00
. resolve_pkg_cache_folder_id_from_pkg_id ( id ) ? ;
Some ( self . cache . package_folder_for_id ( & folder_id ) )
2022-09-13 11:59:01 -04:00
}
2022-09-22 11:17:02 -04:00
fn resolve_package_folder_from_package (
2022-09-13 11:59:01 -04:00
& self ,
name : & str ,
referrer : & ModuleSpecifier ,
2024-07-09 12:15:03 -04:00
) -> Result < PathBuf , PackageFolderResolveError > {
use deno_npm ::resolution ::PackageNotFoundFromReferrerError ;
let Some ( referrer_cache_folder_id ) = self
2022-09-13 11:59:01 -04:00
. cache
2024-05-23 17:26:23 -04:00
. resolve_package_folder_id_from_specifier ( referrer )
2023-08-27 00:04:12 -04:00
else {
2024-07-09 12:15:03 -04:00
return Err (
2024-07-23 20:22:24 -04:00
ReferrerNotFoundError {
2024-07-09 12:15:03 -04:00
referrer : referrer . clone ( ) ,
referrer_extra : None ,
}
. into ( ) ,
) ;
2023-08-27 00:04:12 -04:00
} ;
2024-07-09 12:15:03 -04:00
let resolve_result = self
2024-06-08 20:05:28 -04:00
. resolution
2024-07-09 12:15:03 -04:00
. resolve_package_from_package ( name , & referrer_cache_folder_id ) ;
match resolve_result {
Ok ( pkg ) = > match self . maybe_package_folder ( & pkg . id ) {
Some ( folder ) = > Ok ( folder ) ,
None = > Err (
2024-07-23 20:22:24 -04:00
PackageNotFoundError {
2024-07-09 12:15:03 -04:00
package_name : name . to_string ( ) ,
referrer : referrer . clone ( ) ,
referrer_extra : Some ( format! (
" {} -> {} " ,
referrer_cache_folder_id ,
pkg . id . as_serialized ( )
) ) ,
}
. into ( ) ,
) ,
} ,
Err ( err ) = > match * err {
PackageNotFoundFromReferrerError ::Referrer ( cache_folder_id ) = > Err (
2024-07-23 20:22:24 -04:00
ReferrerNotFoundError {
2024-07-09 12:15:03 -04:00
referrer : referrer . clone ( ) ,
referrer_extra : Some ( cache_folder_id . to_string ( ) ) ,
}
. into ( ) ,
) ,
PackageNotFoundFromReferrerError ::Package {
name ,
referrer : cache_folder_id_referrer ,
} = > Err (
2024-07-23 20:22:24 -04:00
PackageNotFoundError {
2024-07-09 12:15:03 -04:00
package_name : name ,
referrer : referrer . clone ( ) ,
referrer_extra : Some ( cache_folder_id_referrer . to_string ( ) ) ,
}
. into ( ) ,
) ,
} ,
}
2022-09-13 11:59:01 -04:00
}
2023-07-01 21:07:57 -04:00
fn resolve_package_cache_folder_id_from_specifier (
& self ,
specifier : & ModuleSpecifier ,
2023-07-17 14:00:44 -04:00
) -> Result < Option < NpmPackageCacheFolderId > , AnyError > {
Ok (
2024-05-23 17:26:23 -04:00
self
. cache
. resolve_package_folder_id_from_specifier ( specifier ) ,
2023-07-17 14:00:44 -04:00
)
2023-07-01 21:07:57 -04:00
}
2024-06-03 17:17:08 -04:00
async fn cache_packages ( & self ) -> Result < ( ) , AnyError > {
2023-05-17 17:38:50 -04:00
let package_partitions = self
. resolution
. all_system_packages_partitioned ( & self . system_info ) ;
2024-09-24 15:23:57 -04:00
cache_packages ( & package_partitions . packages , & self . tarball_cache ) . await ? ;
2023-05-17 17:38:50 -04:00
// create the copy package folders
for copy in package_partitions . copy_packages {
2024-05-23 17:26:23 -04:00
self
. cache
. ensure_copy_package ( & copy . get_package_cache_folder_id ( ) ) ? ;
2023-05-17 17:38:50 -04:00
}
2024-09-24 15:23:57 -04:00
let mut lifecycle_scripts =
super ::common ::lifecycle_scripts ::LifecycleScripts ::new (
& self . lifecycle_scripts ,
GlobalLifecycleScripts ::new ( self , & self . lifecycle_scripts . root_dir ) ,
) ;
for package in & package_partitions . packages {
let package_folder = self . cache . package_folder_for_nv ( & package . id . nv ) ;
lifecycle_scripts . add ( package , Cow ::Borrowed ( & package_folder ) ) ;
}
lifecycle_scripts . warn_not_run_scripts ( ) ? ;
2023-05-17 17:38:50 -04:00
Ok ( ( ) )
2022-09-13 11:59:01 -04:00
}
2023-01-10 08:35:44 -05:00
fn ensure_read_permission (
& self ,
2024-06-06 23:37:53 -04:00
permissions : & mut dyn NodePermissions ,
2023-01-10 08:35:44 -05:00
path : & Path ,
) -> Result < ( ) , AnyError > {
2023-06-09 14:41:18 -04:00
self
. registry_read_permission_checker
. ensure_registry_read_permission ( permissions , path )
2022-09-13 11:59:01 -04:00
}
}
2024-09-24 15:23:57 -04:00
struct GlobalLifecycleScripts < ' a > {
resolver : & ' a GlobalNpmPackageResolver ,
path_hash : u64 ,
}
impl < ' a > GlobalLifecycleScripts < ' a > {
fn new ( resolver : & ' a GlobalNpmPackageResolver , root_dir : & Path ) -> Self {
let mut hasher = FastInsecureHasher ::new_without_deno_version ( ) ;
hasher . write ( root_dir . to_string_lossy ( ) . as_bytes ( ) ) ;
let path_hash = hasher . finish ( ) ;
Self {
resolver ,
path_hash ,
}
}
fn warned_scripts_file ( & self , package : & NpmResolutionPackage ) -> PathBuf {
self
. package_path ( package )
. join ( format! ( " .scripts-warned- {} " , self . path_hash ) )
}
}
impl < ' a > super ::common ::lifecycle_scripts ::LifecycleScriptsStrategy
for GlobalLifecycleScripts < ' a >
{
fn can_run_scripts ( & self ) -> bool {
false
}
fn package_path ( & self , package : & NpmResolutionPackage ) -> PathBuf {
self . resolver . cache . package_folder_for_nv ( & package . id . nv )
}
fn warn_on_scripts_not_run (
& self ,
packages : & [ ( & NpmResolutionPackage , PathBuf ) ] ,
) -> std ::result ::Result < ( ) , deno_core ::anyhow ::Error > {
log ::warn! ( " {} The following packages contained npm lifecycle scripts ({}) that were not executed: " , colors ::yellow ( " Warning " ) , colors ::gray ( " preinstall/install/postinstall " ) ) ;
for ( package , _ ) in packages {
log ::warn! ( " ┠─ {} " , colors ::gray ( format! ( " npm: {} " , package . id . nv ) ) ) ;
}
log ::warn! ( " ┃ " ) ;
log ::warn! (
" ┠─ {} " ,
colors ::italic ( " This may cause the packages to not work correctly. " )
) ;
log ::warn! ( " ┠─ {} " , colors ::italic ( " Lifecycle scripts are only supported when using a `node_modules` directory. " ) ) ;
log ::warn! (
" ┠─ {} " ,
colors ::italic ( " Enable it in your deno config file: " )
) ;
log ::warn! ( " ┖─ {} " , colors ::bold ( " \" nodeModulesDir \" : \" auto \" " ) ) ;
for ( package , _ ) in packages {
std ::fs ::write ( self . warned_scripts_file ( package ) , " " ) ? ;
}
Ok ( ( ) )
}
fn did_run_scripts (
& self ,
_package : & NpmResolutionPackage ,
) -> std ::result ::Result < ( ) , deno_core ::anyhow ::Error > {
Ok ( ( ) )
}
fn has_warned ( & self , package : & NpmResolutionPackage ) -> bool {
self . warned_scripts_file ( package ) . exists ( )
}
fn has_run ( & self , _package : & NpmResolutionPackage ) -> bool {
false
}
}