2024-01-01 14:58:21 -05:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2021-10-10 17:26:22 -04:00
2024-06-05 11:04:16 -04:00
use async_trait ::async_trait ;
2024-05-13 12:55:31 -04:00
use dashmap ::DashMap ;
2024-05-18 11:42:03 -04:00
use dashmap ::DashSet ;
2023-12-06 19:03:18 -05:00
use deno_ast ::MediaType ;
2024-08-20 10:11:43 -04:00
use deno_config ::workspace ::MappedResolutionDiagnostic ;
2024-07-03 20:54:33 -04:00
use deno_config ::workspace ::MappedResolutionError ;
2023-03-03 17:27:05 -05:00
use deno_core ::anyhow ::anyhow ;
2024-01-27 12:10:36 -05:00
use deno_core ::anyhow ::Context ;
2023-01-24 08:23:19 -05:00
use deno_core ::error ::AnyError ;
2024-11-01 12:27:00 -04:00
use deno_core ::url ::Url ;
2024-06-05 11:04:16 -04:00
use deno_core ::ModuleSourceCode ;
2021-10-10 17:26:22 -04:00
use deno_core ::ModuleSpecifier ;
2023-10-20 00:02:08 -04:00
use deno_graph ::source ::ResolveError ;
2023-02-22 14:15:25 -05:00
use deno_graph ::source ::UnknownBuiltInNodeModuleError ;
2024-06-11 08:55:12 -04:00
use deno_graph ::NpmLoadError ;
use deno_graph ::NpmResolvePkgReqsResult ;
use deno_npm ::resolution ::NpmResolutionError ;
2024-09-28 19:17:48 -04:00
use deno_resolver ::sloppy_imports ::SloppyImportsResolver ;
2024-08-20 10:11:43 -04:00
use deno_runtime ::colors ;
2024-01-27 12:10:36 -05:00
use deno_runtime ::deno_fs ;
2023-10-25 14:39:00 -04:00
use deno_runtime ::deno_fs ::FileSystem ;
2023-02-22 14:15:25 -05:00
use deno_runtime ::deno_node ::is_builtin_node_module ;
2024-11-14 15:24:25 -05:00
use deno_runtime ::deno_node ::DenoFsNodeResolverEnv ;
2023-08-21 05:53:52 -04:00
use deno_semver ::package ::PackageReq ;
2024-11-26 14:38:24 -05:00
use node_resolver ::NodeResolutionKind ;
use node_resolver ::ResolutionMode ;
2024-11-01 12:27:00 -04:00
use std ::borrow ::Cow ;
2023-12-06 19:03:18 -05:00
use std ::path ::Path ;
2023-08-17 12:14:22 -04:00
use std ::path ::PathBuf ;
2021-11-08 20:26:39 -05:00
use std ::sync ::Arc ;
2024-11-01 12:27:00 -04:00
use thiserror ::Error ;
2021-10-10 17:26:22 -04:00
2024-03-07 08:59:57 -05:00
use crate ::args ::DENO_DISABLE_PEDANTIC_NODE_WARNINGS ;
2024-01-27 12:10:36 -05:00
use crate ::node ::CliNodeCodeTranslator ;
2023-09-30 12:06:38 -04:00
use crate ::npm ::CliNpmResolver ;
2023-10-03 19:05:06 -04:00
use crate ::npm ::InnerCliNpmResolverRef ;
2023-04-11 18:10:51 -04:00
use crate ::util ::sync ::AtomicFlag ;
2024-10-14 20:48:39 -04:00
use crate ::util ::text_encoding ::from_utf8_lossy_owned ;
2022-08-24 13:36:05 -04:00
2024-11-14 15:24:25 -05:00
pub type CjsTracker = deno_resolver ::cjs ::CjsTracker < DenoFsNodeResolverEnv > ;
pub type IsCjsResolver =
deno_resolver ::cjs ::IsCjsResolver < DenoFsNodeResolverEnv > ;
pub type IsCjsResolverOptions = deno_resolver ::cjs ::IsCjsResolverOptions ;
pub type CliSloppyImportsResolver =
SloppyImportsResolver < SloppyImportsCachedFs > ;
pub type CliDenoResolver = deno_resolver ::DenoResolver <
CliDenoResolverFs ,
DenoFsNodeResolverEnv ,
SloppyImportsCachedFs ,
> ;
pub type CliNpmReqResolver =
deno_resolver ::npm ::NpmReqResolver < CliDenoResolverFs , DenoFsNodeResolverEnv > ;
2024-01-27 12:10:36 -05:00
pub struct ModuleCodeStringSource {
2024-06-05 11:04:16 -04:00
pub code : ModuleSourceCode ,
2024-01-27 12:10:36 -05:00
pub found_url : ModuleSpecifier ,
pub media_type : MediaType ,
}
2024-09-30 09:33:32 -04:00
#[ derive(Debug, Clone) ]
pub struct CliDenoResolverFs ( pub Arc < dyn FileSystem > ) ;
impl deno_resolver ::fs ::DenoResolverFs for CliDenoResolverFs {
fn read_to_string_lossy ( & self , path : & Path ) -> std ::io ::Result < String > {
self
. 0
. read_text_file_lossy_sync ( path , None )
. map_err ( | e | e . into_io_error ( ) )
}
fn realpath_sync ( & self , path : & Path ) -> std ::io ::Result < PathBuf > {
self . 0. realpath_sync ( path ) . map_err ( | e | e . into_io_error ( ) )
}
2024-11-14 15:24:25 -05:00
fn exists_sync ( & self , path : & Path ) -> bool {
self . 0. exists_sync ( path )
}
2024-09-30 09:33:32 -04:00
fn is_dir_sync ( & self , path : & Path ) -> bool {
self . 0. is_dir_sync ( path )
}
fn read_dir_sync (
& self ,
dir_path : & Path ,
) -> std ::io ::Result < Vec < deno_resolver ::fs ::DirEntry > > {
self
. 0
. read_dir_sync ( dir_path )
. map ( | entries | {
entries
. into_iter ( )
. map ( | e | deno_resolver ::fs ::DirEntry {
name : e . name ,
is_file : e . is_file ,
is_directory : e . is_directory ,
} )
. collect ::< Vec < _ > > ( )
} )
. map_err ( | err | err . into_io_error ( ) )
}
}
2024-11-01 12:27:00 -04:00
#[ derive(Debug, Error) ]
#[ error( " {media_type} files are not supported in npm packages: {specifier} " ) ]
pub struct NotSupportedKindInNpmError {
pub media_type : MediaType ,
pub specifier : Url ,
2024-01-27 12:10:36 -05:00
}
2024-11-01 12:27:00 -04:00
// todo(dsherret): move to module_loader.rs (it seems to be here due to use in standalone)
2024-05-16 03:09:35 -04:00
#[ derive(Clone) ]
2024-01-27 12:10:36 -05:00
pub struct NpmModuleLoader {
2024-11-01 12:27:00 -04:00
cjs_tracker : Arc < CjsTracker > ,
2024-01-27 12:10:36 -05:00
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-11-01 12:27:00 -04:00
node_code_translator : Arc < CliNodeCodeTranslator > ,
2024-01-27 12:10:36 -05:00
}
impl NpmModuleLoader {
pub fn new (
2024-11-01 12:27:00 -04:00
cjs_tracker : Arc < CjsTracker > ,
2024-01-27 12:10:36 -05:00
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-11-01 12:27:00 -04:00
node_code_translator : Arc < CliNodeCodeTranslator > ,
2024-01-27 12:10:36 -05:00
) -> Self {
Self {
2024-11-01 12:27:00 -04:00
cjs_tracker ,
2024-01-27 12:10:36 -05:00
node_code_translator ,
fs ,
}
}
2024-05-18 11:42:03 -04:00
pub async fn load (
2024-01-27 12:10:36 -05:00
& self ,
specifier : & ModuleSpecifier ,
maybe_referrer : Option < & ModuleSpecifier > ,
) -> Result < ModuleCodeStringSource , AnyError > {
let file_path = specifier . to_file_path ( ) . unwrap ( ) ;
let code = self
. fs
2024-06-05 11:04:16 -04:00
. read_file_async ( file_path . clone ( ) , None )
2024-05-18 11:42:03 -04:00
. await
2024-01-27 12:10:36 -05:00
. map_err ( AnyError ::from )
. with_context ( | | {
if file_path . is_dir ( ) {
// directory imports are not allowed when importing from an
// ES module, so provide the user with a helpful error message
let dir_path = file_path ;
let mut msg = " Directory import " . to_string ( ) ;
msg . push_str ( & dir_path . to_string_lossy ( ) ) ;
if let Some ( referrer ) = & maybe_referrer {
msg . push_str ( " is not supported resolving import from " ) ;
msg . push_str ( referrer . as_str ( ) ) ;
let entrypoint_name = [ " index.mjs " , " index.js " , " index.cjs " ]
. iter ( )
. find ( | e | dir_path . join ( e ) . is_file ( ) ) ;
if let Some ( entrypoint_name ) = entrypoint_name {
msg . push_str ( " \n Did you mean to import " ) ;
msg . push_str ( entrypoint_name ) ;
msg . push_str ( " within the directory? " ) ;
}
}
msg
} else {
let mut msg = " Unable to load " . to_string ( ) ;
msg . push_str ( & file_path . to_string_lossy ( ) ) ;
if let Some ( referrer ) = & maybe_referrer {
msg . push_str ( " imported from " ) ;
msg . push_str ( referrer . as_str ( ) ) ;
}
msg
}
} ) ? ;
2024-11-01 12:27:00 -04:00
let media_type = MediaType ::from_specifier ( specifier ) ;
if media_type . is_emittable ( ) {
return Err ( AnyError ::from ( NotSupportedKindInNpmError {
media_type ,
specifier : specifier . clone ( ) ,
} ) ) ;
}
let code = if self . cjs_tracker . is_maybe_cjs ( specifier , media_type ) ? {
2024-01-27 12:10:36 -05:00
// translate cjs to esm if it's cjs and inject node globals
2024-10-14 20:48:39 -04:00
let code = from_utf8_lossy_owned ( code ) ;
2024-06-05 11:04:16 -04:00
ModuleSourceCode ::String (
self
. node_code_translator
2024-11-01 12:27:00 -04:00
. translate_cjs_to_esm ( specifier , Some ( Cow ::Owned ( code ) ) )
2024-06-05 11:04:16 -04:00
. await ?
2024-11-01 12:27:00 -04:00
. into_owned ( )
2024-06-05 11:04:16 -04:00
. into ( ) ,
)
2024-01-27 12:10:36 -05:00
} else {
// esm and json code is untouched
2024-06-05 11:04:16 -04:00
ModuleSourceCode ::Bytes ( code . into_boxed_slice ( ) . into ( ) )
2024-01-27 12:10:36 -05:00
} ;
2024-11-01 12:27:00 -04:00
2024-01-27 12:10:36 -05:00
Ok ( ModuleCodeStringSource {
2024-06-05 11:04:16 -04:00
code ,
2024-01-27 12:10:36 -05:00
found_url : specifier . clone ( ) ,
media_type : MediaType ::from_specifier ( specifier ) ,
} )
}
}
2024-11-14 15:24:25 -05:00
pub struct CliResolverOptions {
pub deno_resolver : Arc < CliDenoResolver > ,
pub npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
pub bare_node_builtins_enabled : bool ,
2024-01-27 12:10:36 -05:00
}
2022-11-02 10:47:02 -04:00
/// A resolver that takes care of resolution, taking into account loaded
/// import map, JSX settings.
2023-04-14 16:22:33 -04:00
#[ derive(Debug) ]
2024-11-13 10:10:09 -05:00
pub struct CliResolver {
2024-11-14 15:24:25 -05:00
deno_resolver : Arc < CliDenoResolver > ,
2024-07-03 20:54:33 -04:00
npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
2024-06-11 08:55:12 -04:00
found_package_json_dep_flag : AtomicFlag ,
2023-10-20 00:02:08 -04:00
bare_node_builtins_enabled : bool ,
2024-08-20 10:11:43 -04:00
warned_pkgs : DashSet < PackageReq > ,
2023-02-22 14:15:25 -05:00
}
2024-11-13 10:10:09 -05:00
impl CliResolver {
pub fn new ( options : CliResolverOptions ) -> Self {
2023-02-15 11:30:54 -05:00
Self {
2024-11-14 15:24:25 -05:00
deno_resolver : options . deno_resolver ,
2024-07-03 20:54:33 -04:00
npm_resolver : options . npm_resolver ,
2023-04-11 18:10:51 -04:00
found_package_json_dep_flag : Default ::default ( ) ,
2023-10-20 00:02:08 -04:00
bare_node_builtins_enabled : options . bare_node_builtins_enabled ,
2024-08-20 10:11:43 -04:00
warned_pkgs : Default ::default ( ) ,
2022-01-31 17:33:57 -05:00
}
2021-10-10 17:26:22 -04:00
}
2021-11-08 20:26:39 -05:00
2024-11-14 15:24:25 -05:00
// todo(dsherret): move this off CliResolver as CliResolver is acting
// like a factory by doing this (it's beyond its responsibility)
2024-06-05 11:04:16 -04:00
pub fn create_graph_npm_resolver ( & self ) -> WorkerCliNpmGraphResolver {
WorkerCliNpmGraphResolver {
npm_resolver : self . npm_resolver . as_ref ( ) ,
2024-06-11 08:55:12 -04:00
found_package_json_dep_flag : & self . found_package_json_dep_flag ,
2024-06-05 11:04:16 -04:00
bare_node_builtins_enabled : self . bare_node_builtins_enabled ,
}
2023-02-22 14:15:25 -05:00
}
2024-04-30 14:12:35 -04:00
2024-11-13 10:10:09 -05:00
pub fn resolve (
2021-11-08 20:26:39 -05:00
& self ,
2024-09-04 10:00:44 -04:00
raw_specifier : & str ,
2024-11-26 14:38:24 -05:00
referrer : & ModuleSpecifier ,
referrer_range_start : deno_graph ::Position ,
resolution_mode : ResolutionMode ,
resolution_kind : NodeResolutionKind ,
2023-10-20 00:02:08 -04:00
) -> Result < ModuleSpecifier , ResolveError > {
2024-11-14 15:24:25 -05:00
let resolution = self
. deno_resolver
2024-11-26 14:38:24 -05:00
. resolve ( raw_specifier , referrer , resolution_mode , resolution_kind )
2024-11-14 15:24:25 -05:00
. map_err ( | err | match err . into_kind ( ) {
deno_resolver ::DenoResolveErrorKind ::MappedResolution (
mapped_resolution_error ,
) = > match mapped_resolution_error {
MappedResolutionError ::Specifier ( e ) = > ResolveError ::Specifier ( e ) ,
// deno_graph checks specifically for an ImportMapError
MappedResolutionError ::ImportMap ( e ) = > ResolveError ::Other ( e . into ( ) ) ,
err = > ResolveError ::Other ( err . into ( ) ) ,
} ,
err = > ResolveError ::Other ( err . into ( ) ) ,
} ) ? ;
2024-07-23 20:22:24 -04:00
2024-11-14 15:24:25 -05:00
if resolution . found_package_json_dep {
// mark that we need to do an "npm install" later
self . found_package_json_dep_flag . raise ( ) ;
2024-07-23 20:22:24 -04:00
}
2024-11-14 15:24:25 -05:00
if let Some ( diagnostic ) = resolution . maybe_diagnostic {
match & * diagnostic {
MappedResolutionDiagnostic ::ConstraintNotMatchedLocalVersion {
reference ,
2024-07-03 20:54:33 -04:00
..
} = > {
2024-11-14 15:24:25 -05:00
if self . warned_pkgs . insert ( reference . req ( ) . clone ( ) ) {
log ::warn! (
2024-11-26 14:38:24 -05:00
" {} {} \n at {}:{} " ,
2024-11-14 15:24:25 -05:00
colors ::yellow ( " Warning " ) ,
diagnostic ,
2024-11-26 14:38:24 -05:00
referrer ,
referrer_range_start ,
2024-11-14 15:24:25 -05:00
) ;
2023-10-25 14:39:00 -04:00
}
}
2024-05-21 12:54:15 -04:00
}
2023-10-25 14:39:00 -04:00
}
2024-11-14 15:24:25 -05:00
Ok ( resolution . url )
2021-11-08 20:26:39 -05:00
}
}
2023-02-22 14:15:25 -05:00
2024-06-05 11:04:16 -04:00
#[ derive(Debug) ]
pub struct WorkerCliNpmGraphResolver < ' a > {
npm_resolver : Option < & ' a Arc < dyn CliNpmResolver > > ,
2024-06-11 08:55:12 -04:00
found_package_json_dep_flag : & ' a AtomicFlag ,
2024-06-05 11:04:16 -04:00
bare_node_builtins_enabled : bool ,
}
#[ async_trait(?Send) ]
impl < ' a > deno_graph ::source ::NpmResolver for WorkerCliNpmGraphResolver < ' a > {
2023-02-22 14:15:25 -05:00
fn resolve_builtin_node_module (
& self ,
specifier : & ModuleSpecifier ,
) -> Result < Option < String > , UnknownBuiltInNodeModuleError > {
if specifier . scheme ( ) ! = " node " {
return Ok ( None ) ;
}
let module_name = specifier . path ( ) . to_string ( ) ;
if is_builtin_node_module ( & module_name ) {
Ok ( Some ( module_name ) )
} else {
Err ( UnknownBuiltInNodeModuleError { module_name } )
}
}
2023-10-20 00:02:08 -04:00
fn on_resolve_bare_builtin_node_module (
& self ,
module_name : & str ,
range : & deno_graph ::Range ,
) {
2024-11-26 14:38:24 -05:00
let start = range . range . start ;
let specifier = & range . specifier ;
2024-03-07 08:59:57 -05:00
if ! * DENO_DISABLE_PEDANTIC_NODE_WARNINGS {
2024-11-26 14:38:24 -05:00
log ::warn! ( " {} Resolving \" {module_name} \" as \" node:{module_name} \" at {specifier}:{start}. If you want to use a built-in Node module, add a \" node: \" prefix. " , colors ::yellow ( " Warning " ) )
2024-03-07 08:59:57 -05:00
}
2023-10-20 00:02:08 -04:00
}
2024-06-11 08:55:12 -04:00
fn load_and_cache_npm_package_info ( & self , package_name : & str ) {
2024-06-05 11:04:16 -04:00
match self . npm_resolver {
2023-09-30 12:06:38 -04:00
Some ( npm_resolver ) if npm_resolver . as_managed ( ) . is_some ( ) = > {
let npm_resolver = npm_resolver . clone ( ) ;
2024-06-05 11:04:16 -04:00
let package_name = package_name . to_string ( ) ;
2024-06-11 08:55:12 -04:00
deno_core ::unsync ::spawn ( async move {
2023-09-30 12:06:38 -04:00
if let Some ( managed ) = npm_resolver . as_managed ( ) {
2024-06-11 08:55:12 -04:00
let _ignore = managed . cache_package_info ( & package_name ) . await ;
2023-09-30 12:06:38 -04:00
}
2024-06-11 08:55:12 -04:00
} ) ;
2023-09-30 12:06:38 -04:00
}
2024-06-11 08:55:12 -04:00
_ = > { }
2023-02-22 14:15:25 -05:00
}
}
2024-06-05 11:04:16 -04:00
async fn resolve_pkg_reqs (
& self ,
2024-06-11 08:55:12 -04:00
package_reqs : & [ PackageReq ] ,
) -> NpmResolvePkgReqsResult {
2023-09-30 12:06:38 -04:00
match & self . npm_resolver {
2024-06-05 11:04:16 -04:00
Some ( npm_resolver ) = > {
let npm_resolver = match npm_resolver . as_inner ( ) {
InnerCliNpmResolverRef ::Managed ( npm_resolver ) = > npm_resolver ,
// if we are using byonm, then this should never be called because
// we don't use deno_graph's npm resolution in this case
InnerCliNpmResolverRef ::Byonm ( _ ) = > unreachable! ( ) ,
} ;
2024-06-11 08:55:12 -04:00
let top_level_result = if self . found_package_json_dep_flag . is_raised ( ) {
2024-07-02 18:00:16 -04:00
npm_resolver
. ensure_top_level_package_json_install ( )
. await
. map ( | _ | ( ) )
2024-06-11 08:55:12 -04:00
} else {
Ok ( ( ) )
2024-06-05 11:04:16 -04:00
} ;
2024-06-11 08:55:12 -04:00
let result = npm_resolver . add_package_reqs_raw ( package_reqs ) . await ;
NpmResolvePkgReqsResult {
results : result
. results
. into_iter ( )
. map ( | r | {
r . map_err ( | err | match err {
NpmResolutionError ::Registry ( e ) = > {
NpmLoadError ::RegistryInfo ( Arc ::new ( e . into ( ) ) )
}
NpmResolutionError ::Resolution ( e ) = > {
NpmLoadError ::PackageReqResolution ( Arc ::new ( e . into ( ) ) )
}
NpmResolutionError ::DependencyEntry ( e ) = > {
NpmLoadError ::PackageReqResolution ( Arc ::new ( e . into ( ) ) )
}
} )
} )
. collect ( ) ,
dep_graph_result : match top_level_result {
Ok ( ( ) ) = > result . dependencies_result . map_err ( Arc ::new ) ,
Err ( err ) = > Err ( Arc ::new ( err ) ) ,
} ,
}
}
None = > {
let err = Arc ::new ( anyhow! (
" npm specifiers were requested; but --no-npm is specified "
) ) ;
NpmResolvePkgReqsResult {
results : package_reqs
. iter ( )
. map ( | _ | Err ( NpmLoadError ::RegistryInfo ( err . clone ( ) ) ) )
. collect ( ) ,
dep_graph_result : Err ( err ) ,
2023-10-03 19:05:06 -04:00
}
2024-06-05 11:04:16 -04:00
}
2023-04-06 21:41:19 -04:00
}
2023-02-22 14:15:25 -05:00
}
2023-10-20 00:02:08 -04:00
fn enables_bare_builtin_node_module ( & self ) -> bool {
self . bare_node_builtins_enabled
}
2023-02-22 14:15:25 -05:00
}
2023-02-23 12:33:23 -05:00
2023-12-06 19:03:18 -05:00
#[ derive(Debug) ]
2024-09-28 19:17:48 -04:00
pub struct SloppyImportsCachedFs {
fs : Arc < dyn deno_fs ::FileSystem > ,
cache : Option <
DashMap <
PathBuf ,
Option < deno_resolver ::sloppy_imports ::SloppyImportsFsEntry > ,
> ,
> ,
2023-12-06 19:03:18 -05:00
}
2024-09-28 19:17:48 -04:00
impl SloppyImportsCachedFs {
2023-12-06 19:03:18 -05:00
pub fn new ( fs : Arc < dyn FileSystem > ) -> Self {
Self {
2024-05-13 12:55:31 -04:00
fs ,
cache : Some ( Default ::default ( ) ) ,
2023-12-06 19:03:18 -05:00
}
}
2024-05-13 12:55:31 -04:00
pub fn new_without_stat_cache ( fs : Arc < dyn FileSystem > ) -> Self {
Self { fs , cache : None }
2023-12-08 09:57:06 -05:00
}
2024-09-28 19:17:48 -04:00
}
2023-12-08 09:57:06 -05:00
2024-09-28 19:17:48 -04:00
impl deno_resolver ::sloppy_imports ::SloppyImportResolverFs
for SloppyImportsCachedFs
{
fn stat_sync (
2024-05-13 12:55:31 -04:00
& self ,
2024-09-28 19:17:48 -04:00
path : & Path ,
) -> Option < deno_resolver ::sloppy_imports ::SloppyImportsFsEntry > {
2024-05-13 12:55:31 -04:00
if let Some ( cache ) = & self . cache {
if let Some ( entry ) = cache . get ( path ) {
return * entry ;
}
}
2024-09-28 19:17:48 -04:00
let entry = self . fs . stat_sync ( path ) . ok ( ) . and_then ( | stat | {
if stat . is_file {
Some ( deno_resolver ::sloppy_imports ::SloppyImportsFsEntry ::File )
} else if stat . is_directory {
Some ( deno_resolver ::sloppy_imports ::SloppyImportsFsEntry ::Dir )
} else {
None
}
} ) ;
2024-05-13 12:55:31 -04:00
if let Some ( cache ) = & self . cache {
cache . insert ( path . to_owned ( ) , entry ) ;
}
entry
2023-12-06 19:03:18 -05:00
}
}