2025-01-01 04:12:39 +09:00
// Copyright 2018-2025 the Deno authors. MIT license.
2021-10-11 08:26:22 +11:00
2024-12-30 12:38:20 -05:00
use std ::borrow ::Cow ;
use std ::sync ::Arc ;
2024-06-05 11:04:16 -04:00
use async_trait ::async_trait ;
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 ;
2024-01-27 22:40:36 +05:30
use deno_core ::anyhow ::Context ;
2023-01-24 14:23:19 +01: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-11 08:26:22 +11:00
use deno_core ::ModuleSpecifier ;
2025-01-08 14:52:32 -08:00
use deno_error ::JsErrorBox ;
2023-10-20 13:02:08 +09: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 ;
2025-01-14 10:01:05 -05:00
use deno_resolver ::npm ::DenoInNpmPackageChecker ;
2025-01-09 18:30:48 -05:00
use deno_resolver ::sloppy_imports ::SloppyImportsCachedFs ;
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 22:40:36 +05:30
use deno_runtime ::deno_fs ;
2023-02-22 14:15:25 -05:00
use deno_runtime ::deno_node ::is_builtin_node_module ;
2024-12-30 12:38:20 -05:00
use deno_runtime ::deno_node ::RealIsBuiltInNodeModuleChecker ;
2023-08-21 11:53:52 +02: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 thiserror ::Error ;
2021-10-11 08:26:22 +11:00
2024-12-10 18:24:23 -08:00
use crate ::args ::NpmCachingStrategy ;
2024-03-07 13:59:57 +00:00
use crate ::args ::DENO_DISABLE_PEDANTIC_NODE_WARNINGS ;
2024-01-27 22:40:36 +05:30
use crate ::node ::CliNodeCodeTranslator ;
2025-01-13 17:35:18 -05:00
use crate ::npm ::installer ::NpmInstaller ;
use crate ::npm ::installer ::PackageCaching ;
2025-01-14 10:01:05 -05:00
use crate ::npm ::CliNpmResolver ;
2024-12-31 12:13:39 -05:00
use crate ::sys ::CliSys ;
2023-04-11 18:10:51 -04:00
use crate ::util ::sync ::AtomicFlag ;
2024-11-27 21:28:41 -05:00
use crate ::util ::text_encoding ::from_utf8_lossy_cow ;
2022-08-24 19:36:05 +02:00
2025-01-14 10:01:05 -05:00
pub type CliCjsTracker =
deno_resolver ::cjs ::CjsTracker < DenoInNpmPackageChecker , CliSys > ;
pub type CliIsCjsResolver =
deno_resolver ::cjs ::IsCjsResolver < DenoInNpmPackageChecker , CliSys > ;
2025-01-09 18:30:48 -05:00
pub type CliSloppyImportsCachedFs = SloppyImportsCachedFs < CliSys > ;
2024-11-14 15:24:25 -05:00
pub type CliSloppyImportsResolver =
2025-01-09 18:30:48 -05:00
SloppyImportsResolver < CliSloppyImportsCachedFs > ;
2024-11-14 15:24:25 -05:00
pub type CliDenoResolver = deno_resolver ::DenoResolver <
2025-01-14 10:01:05 -05:00
DenoInNpmPackageChecker ,
2024-12-30 12:38:20 -05:00
RealIsBuiltInNodeModuleChecker ,
2025-01-14 10:01:05 -05:00
CliNpmResolver ,
2025-01-09 18:30:48 -05:00
CliSloppyImportsCachedFs ,
2024-12-31 11:29:07 -05:00
CliSys ,
2024-11-14 15:24:25 -05:00
> ;
2025-01-14 10:01:05 -05:00
pub type CliNpmReqResolver = deno_resolver ::npm ::NpmReqResolver <
DenoInNpmPackageChecker ,
RealIsBuiltInNodeModuleChecker ,
CliNpmResolver ,
CliSys ,
> ;
2024-11-14 15:24:25 -05:00
2024-01-27 22:40:36 +05:30
pub struct ModuleCodeStringSource {
2024-06-05 11:04:16 -04:00
pub code : ModuleSourceCode ,
2024-01-27 22:40:36 +05:30
pub found_url : ModuleSpecifier ,
pub media_type : MediaType ,
}
2025-01-08 14:52:32 -08:00
#[ derive(Debug, Error, deno_error::JsError) ]
#[ class(type) ]
2024-11-01 12:27:00 -04:00
#[ error( " {media_type} files are not supported in npm packages: {specifier} " ) ]
pub struct NotSupportedKindInNpmError {
pub media_type : MediaType ,
pub specifier : Url ,
2024-01-27 22:40:36 +05:30
}
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 00:09:35 -07:00
#[ derive(Clone) ]
2024-01-27 22:40:36 +05:30
pub struct NpmModuleLoader {
2025-01-14 10:01:05 -05:00
cjs_tracker : Arc < CliCjsTracker > ,
2024-01-27 22:40:36 +05:30
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-11-01 12:27:00 -04:00
node_code_translator : Arc < CliNodeCodeTranslator > ,
2024-01-27 22:40:36 +05:30
}
impl NpmModuleLoader {
pub fn new (
2025-01-14 10:01:05 -05:00
cjs_tracker : Arc < CliCjsTracker > ,
2024-01-27 22:40:36 +05:30
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-11-01 12:27:00 -04:00
node_code_translator : Arc < CliNodeCodeTranslator > ,
2024-01-27 22:40:36 +05:30
) -> Self {
Self {
2024-11-01 12:27:00 -04:00
cjs_tracker ,
2024-01-27 22:40:36 +05:30
node_code_translator ,
fs ,
}
}
2024-05-18 11:42:03 -04:00
pub async fn load (
2024-01-27 22:40:36 +05:30
& 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 22:40:36 +05:30
. 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 22:40:36 +05:30
// translate cjs to esm if it's cjs and inject node globals
2024-11-27 21:28:41 -05:00
let code = from_utf8_lossy_cow ( code ) ;
2024-06-05 11:04:16 -04:00
ModuleSourceCode ::String (
self
. node_code_translator
2024-11-27 21:28:41 -05:00
. translate_cjs_to_esm ( specifier , Some ( 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 22:40:36 +05:30
} else {
// esm and json code is untouched
2024-11-27 21:28:41 -05:00
ModuleSourceCode ::Bytes ( match code {
Cow ::Owned ( bytes ) = > bytes . into_boxed_slice ( ) . into ( ) ,
Cow ::Borrowed ( bytes ) = > bytes . into ( ) ,
} )
2024-01-27 22:40:36 +05:30
} ;
2024-11-01 12:27:00 -04:00
2024-01-27 22:40:36 +05:30
Ok ( ModuleCodeStringSource {
2024-06-05 11:04:16 -04:00
code ,
2024-01-27 22:40:36 +05:30
found_url : specifier . clone ( ) ,
media_type : MediaType ::from_specifier ( specifier ) ,
} )
}
}
2025-01-13 17:35:18 -05:00
#[ derive(Debug, Default) ]
pub struct FoundPackageJsonDepFlag ( AtomicFlag ) ;
2024-01-27 22:40:36 +05:30
2022-11-02 15:47:02 +01: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 > ,
2025-01-13 17:35:18 -05:00
found_package_json_dep_flag : Arc < FoundPackageJsonDepFlag > ,
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 {
2025-01-13 17:35:18 -05:00
pub fn new (
deno_resolver : Arc < CliDenoResolver > ,
found_package_json_dep_flag : Arc < FoundPackageJsonDepFlag > ,
) -> Self {
2023-02-15 11:30:54 -05:00
Self {
2025-01-13 17:35:18 -05:00
deno_resolver ,
found_package_json_dep_flag ,
2024-08-20 10:11:43 -04:00
warned_pkgs : Default ::default ( ) ,
2022-02-01 09:33:57 +11:00
}
2021-10-11 08:26:22 +11:00
}
2021-11-09 12:26:39 +11:00
2024-11-13 10:10:09 -05:00
pub fn resolve (
2021-11-09 12:26:39 +11:00
& self ,
2024-09-04 16:00:44 +02: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 13:02:08 +09: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
2025-01-08 14:52:32 -08:00
MappedResolutionError ::ImportMap ( e ) = > ResolveError ::ImportMap ( e ) ,
MappedResolutionError ::Workspace ( e ) = > {
ResolveError ::Other ( JsErrorBox ::from_err ( e ) )
}
2024-11-14 15:24:25 -05:00
} ,
2025-01-08 14:52:32 -08:00
err = > ResolveError ::Other ( JsErrorBox ::from_err ( err ) ) ,
2024-11-14 15:24:25 -05:00
} ) ? ;
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
2025-01-13 17:35:18 -05:00
self . found_package_json_dep_flag . 0. 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 17:54:15 +01:00
}
2023-10-25 14:39:00 -04:00
}
2024-11-14 15:24:25 -05:00
Ok ( resolution . url )
2021-11-09 12:26:39 +11:00
}
}
2023-02-22 14:15:25 -05:00
2024-06-05 11:04:16 -04:00
#[ derive(Debug) ]
2025-01-13 17:35:18 -05:00
pub struct CliNpmGraphResolver {
npm_installer : Option < Arc < NpmInstaller > > ,
found_package_json_dep_flag : Arc < FoundPackageJsonDepFlag > ,
2024-06-05 11:04:16 -04:00
bare_node_builtins_enabled : bool ,
2024-12-10 18:24:23 -08:00
npm_caching : NpmCachingStrategy ,
2024-06-05 11:04:16 -04:00
}
2025-01-13 17:35:18 -05:00
impl CliNpmGraphResolver {
pub fn new (
npm_installer : Option < Arc < NpmInstaller > > ,
found_package_json_dep_flag : Arc < FoundPackageJsonDepFlag > ,
bare_node_builtins_enabled : bool ,
npm_caching : NpmCachingStrategy ,
) -> Self {
Self {
npm_installer ,
found_package_json_dep_flag ,
bare_node_builtins_enabled ,
npm_caching ,
}
}
}
2024-06-05 11:04:16 -04:00
#[ async_trait(?Send) ]
2025-01-13 17:35:18 -05:00
impl deno_graph ::source ::NpmResolver for CliNpmGraphResolver {
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 13:02:08 +09: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 13:59:57 +00: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 13:59:57 +00:00
}
2023-10-20 13:02:08 +09:00
}
2024-06-11 08:55:12 -04:00
fn load_and_cache_npm_package_info ( & self , package_name : & str ) {
2025-01-13 17:35:18 -05:00
if let Some ( npm_installer ) = & self . npm_installer {
let npm_installer = npm_installer . clone ( ) ;
let package_name = package_name . to_string ( ) ;
deno_core ::unsync ::spawn ( async move {
let _ignore = npm_installer . cache_package_info ( & package_name ) . await ;
} ) ;
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 {
2025-01-13 17:35:18 -05:00
match & self . npm_installer {
Some ( npm_installer ) = > {
let top_level_result = if self . found_package_json_dep_flag . 0. is_raised ( )
{
npm_installer
2024-07-02 15:00:16 -07:00
. 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
2025-01-13 17:35:18 -05:00
let result = npm_installer
2024-12-10 18:24:23 -08:00
. add_package_reqs_raw (
package_reqs ,
match self . npm_caching {
2025-01-13 17:35:18 -05:00
NpmCachingStrategy ::Eager = > Some ( PackageCaching ::All ) ,
2024-12-10 18:24:23 -08:00
NpmCachingStrategy ::Lazy = > {
2025-01-13 17:35:18 -05:00
Some ( PackageCaching ::Only ( package_reqs . into ( ) ) )
2024-12-10 18:24:23 -08:00
}
NpmCachingStrategy ::Manual = > None ,
} ,
)
. await ;
2024-06-11 08:55:12 -04:00
NpmResolvePkgReqsResult {
results : result
. results
. into_iter ( )
. map ( | r | {
r . map_err ( | err | match err {
NpmResolutionError ::Registry ( e ) = > {
2025-01-08 14:52:32 -08:00
NpmLoadError ::RegistryInfo ( Arc ::new ( e ) )
2024-06-11 08:55:12 -04:00
}
NpmResolutionError ::Resolution ( e ) = > {
2025-01-08 14:52:32 -08:00
NpmLoadError ::PackageReqResolution ( Arc ::new ( e ) )
2024-06-11 08:55:12 -04:00
}
NpmResolutionError ::DependencyEntry ( e ) = > {
2025-01-08 14:52:32 -08:00
NpmLoadError ::PackageReqResolution ( Arc ::new ( e ) )
2024-06-11 08:55:12 -04:00
}
} )
} )
. collect ( ) ,
dep_graph_result : match top_level_result {
2025-01-08 14:52:32 -08:00
Ok ( ( ) ) = > result
. dependencies_result
. map_err ( | e | Arc ::new ( e ) as Arc < dyn deno_error ::JsErrorClass > ) ,
2024-06-11 08:55:12 -04:00
Err ( err ) = > Err ( Arc ::new ( err ) ) ,
} ,
}
}
None = > {
2025-01-08 14:52:32 -08:00
let err = Arc ::new ( JsErrorBox ::generic (
" npm specifiers were requested; but --no-npm is specified " ,
2024-06-11 08:55:12 -04:00
) ) ;
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 13:02:08 +09:00
fn enables_bare_builtin_node_module ( & self ) -> bool {
self . bare_node_builtins_enabled
}
2023-02-22 14:15:25 -05:00
}