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-07-03 20:54:33 -04:00
use deno_config ::workspace ::MappedResolution ;
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 ;
use deno_config ::workspace ::WorkspaceResolver ;
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-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-24 09:37:02 -04:00
use deno_graph ::source ::ResolutionMode ;
2023-10-20 00:02:08 -04:00
use deno_graph ::source ::ResolveError ;
2021-10-10 17:26:22 -04:00
use deno_graph ::source ::Resolver ;
2023-02-22 14:15:25 -05:00
use deno_graph ::source ::UnknownBuiltInNodeModuleError ;
2022-11-02 10:47:02 -04:00
use deno_graph ::source ::DEFAULT_JSX_IMPORT_SOURCE_MODULE ;
2024-06-11 08:55:12 -04:00
use deno_graph ::NpmLoadError ;
use deno_graph ::NpmResolvePkgReqsResult ;
use deno_npm ::resolution ::NpmResolutionError ;
2024-07-23 17:34:46 -04:00
use deno_package_json ::PackageJsonDepValue ;
2024-09-28 19:17:48 -04:00
use deno_resolver ::sloppy_imports ::SloppyImportsResolutionMode ;
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 ;
2023-10-25 14:39:00 -04:00
use deno_runtime ::deno_node ::NodeResolver ;
use deno_semver ::npm ::NpmPackageReqReference ;
2023-08-21 05:53:52 -04:00
use deno_semver ::package ::PackageReq ;
2024-07-25 19:08:14 -04:00
use node_resolver ::errors ::ClosestPkgJsonError ;
use node_resolver ::errors ::NodeResolveError ;
use node_resolver ::errors ::NodeResolveErrorKind ;
use node_resolver ::errors ::PackageFolderResolveErrorKind ;
use node_resolver ::errors ::PackageFolderResolveIoError ;
use node_resolver ::errors ::PackageNotFoundError ;
use node_resolver ::errors ::PackageResolveErrorKind ;
use node_resolver ::errors ::UrlToNodeResolutionError ;
use node_resolver ::NodeModuleKind ;
use node_resolver ::NodeResolution ;
use node_resolver ::NodeResolutionMode ;
use node_resolver ::PackageJson ;
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 ;
2021-10-10 17:26:22 -04:00
2022-11-25 17:00:28 -05:00
use crate ::args ::JsxImportSourceConfig ;
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 ;
2024-10-14 20:48:39 -04:00
use crate ::util ::path ::specifier_has_extension ;
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-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 ( ) )
}
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-03-05 19:23:51 -05:00
#[ derive(Debug) ]
2024-01-27 12:10:36 -05:00
pub struct CliNodeResolver {
2024-07-10 14:46:25 -04:00
cjs_resolutions : Arc < CjsResolutionStore > ,
2024-03-05 19:23:51 -05:00
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-01-27 12:10:36 -05:00
node_resolver : Arc < NodeResolver > ,
2024-09-29 20:07:50 -04:00
npm_resolver : Arc < dyn CliNpmResolver > ,
2024-01-27 12:10:36 -05:00
}
impl CliNodeResolver {
pub fn new (
2024-07-10 14:46:25 -04:00
cjs_resolutions : Arc < CjsResolutionStore > ,
2024-03-05 19:23:51 -05:00
fs : Arc < dyn deno_fs ::FileSystem > ,
2024-01-27 12:10:36 -05:00
node_resolver : Arc < NodeResolver > ,
npm_resolver : Arc < dyn CliNpmResolver > ,
) -> Self {
Self {
cjs_resolutions ,
2024-03-05 19:23:51 -05:00
fs ,
2024-01-27 12:10:36 -05:00
node_resolver ,
npm_resolver ,
}
}
2024-05-16 03:09:35 -04:00
pub fn in_npm_package ( & self , specifier : & ModuleSpecifier ) -> bool {
self . npm_resolver . in_npm_package ( specifier )
2024-01-27 12:10:36 -05:00
}
2024-03-05 19:23:51 -05:00
pub fn get_closest_package_json (
& self ,
referrer : & ModuleSpecifier ,
2024-07-09 12:15:03 -04:00
) -> Result < Option < Arc < PackageJson > > , ClosestPkgJsonError > {
2024-06-26 17:24:10 -04:00
self . node_resolver . get_closest_package_json ( referrer )
2024-03-05 19:23:51 -05:00
}
2024-07-23 20:22:24 -04:00
pub fn resolve_if_for_npm_pkg (
2024-01-27 12:10:36 -05:00
& self ,
specifier : & str ,
referrer : & ModuleSpecifier ,
2024-03-05 19:23:51 -05:00
mode : NodeResolutionMode ,
2024-07-23 20:22:24 -04:00
) -> Result < Option < NodeResolution > , AnyError > {
let resolution_result = self . resolve ( specifier , referrer , mode ) ;
match resolution_result {
Ok ( res ) = > Ok ( Some ( res ) ) ,
Err ( err ) = > {
let err = err . into_kind ( ) ;
match err {
NodeResolveErrorKind ::RelativeJoin ( _ )
| NodeResolveErrorKind ::PackageImportsResolve ( _ )
| NodeResolveErrorKind ::UnsupportedEsmUrlScheme ( _ )
| NodeResolveErrorKind ::DataUrlReferrer ( _ )
| NodeResolveErrorKind ::TypesNotFound ( _ )
| NodeResolveErrorKind ::FinalizeResolution ( _ )
| NodeResolveErrorKind ::UrlToNodeResolution ( _ ) = > Err ( err . into ( ) ) ,
NodeResolveErrorKind ::PackageResolve ( err ) = > {
let err = err . into_kind ( ) ;
match err {
PackageResolveErrorKind ::ClosestPkgJson ( _ )
| PackageResolveErrorKind ::InvalidModuleSpecifier ( _ )
| PackageResolveErrorKind ::ExportsResolve ( _ )
| PackageResolveErrorKind ::SubpathResolve ( _ ) = > Err ( err . into ( ) ) ,
PackageResolveErrorKind ::PackageFolderResolve ( err ) = > {
match err . as_kind ( ) {
PackageFolderResolveErrorKind ::Io (
PackageFolderResolveIoError { package_name , .. } ,
)
| PackageFolderResolveErrorKind ::PackageNotFound (
PackageNotFoundError { package_name , .. } ,
) = > {
if self . in_npm_package ( referrer ) {
return Err ( err . into ( ) ) ;
}
if let Some ( byonm_npm_resolver ) =
self . npm_resolver . as_byonm ( )
{
if byonm_npm_resolver
. find_ancestor_package_json_with_dep (
package_name ,
referrer ,
)
. is_some ( )
{
return Err ( anyhow! (
concat! (
" Could not resolve \" {} \" , but found it in a package.json. " ,
" Deno expects the node_modules/ directory to be up to date. " ,
2024-08-27 22:31:41 -04:00
" Did you forget to run `deno install`? "
2024-07-23 20:22:24 -04:00
) ,
specifier
) ) ;
}
}
Ok ( None )
}
PackageFolderResolveErrorKind ::ReferrerNotFound ( _ ) = > {
if self . in_npm_package ( referrer ) {
return Err ( err . into ( ) ) ;
}
Ok ( None )
}
}
}
}
}
}
}
2024-01-27 12:10:36 -05:00
}
}
2024-03-05 19:23:51 -05:00
pub fn resolve (
& self ,
specifier : & str ,
referrer : & ModuleSpecifier ,
mode : NodeResolutionMode ,
2024-07-23 20:22:24 -04:00
) -> Result < NodeResolution , NodeResolveError > {
2024-10-14 20:48:39 -04:00
let referrer_kind = if self . cjs_resolutions . is_known_cjs ( referrer ) {
2024-07-10 14:46:25 -04:00
NodeModuleKind ::Cjs
} else {
NodeModuleKind ::Esm
} ;
2024-07-23 20:22:24 -04:00
let res =
2024-07-09 12:15:03 -04:00
self
. node_resolver
2024-07-16 18:32:41 -04:00
. resolve ( specifier , referrer , referrer_kind , mode ) ? ;
2024-07-23 20:22:24 -04:00
Ok ( self . handle_node_resolution ( res ) )
2024-03-05 19:23:51 -05:00
}
2024-01-27 12:10:36 -05:00
pub fn resolve_req_reference (
& self ,
req_ref : & NpmPackageReqReference ,
referrer : & ModuleSpecifier ,
2024-03-05 19:23:51 -05:00
mode : NodeResolutionMode ,
) -> Result < NodeResolution , AnyError > {
2024-07-03 20:54:33 -04:00
self . resolve_req_with_sub_path (
req_ref . req ( ) ,
2024-03-05 19:23:51 -05:00
req_ref . sub_path ( ) ,
referrer ,
mode ,
2024-07-03 20:54:33 -04:00
)
}
pub fn resolve_req_with_sub_path (
& self ,
req : & PackageReq ,
sub_path : Option < & str > ,
referrer : & ModuleSpecifier ,
mode : NodeResolutionMode ,
) -> Result < NodeResolution , AnyError > {
let package_folder = self
. npm_resolver
. resolve_pkg_folder_from_deno_module_req ( req , referrer ) ? ;
2024-07-23 20:22:24 -04:00
let resolution_result = self . resolve_package_sub_path_from_deno_module (
& package_folder ,
sub_path ,
Some ( referrer ) ,
mode ,
) ;
match resolution_result {
Ok ( resolution ) = > Ok ( resolution ) ,
Err ( err ) = > {
2024-03-05 19:23:51 -05:00
if self . npm_resolver . as_byonm ( ) . is_some ( ) {
let package_json_path = package_folder . join ( " package.json " ) ;
if ! self . fs . exists_sync ( & package_json_path ) {
return Err ( anyhow! (
2024-08-30 17:58:24 -04:00
" Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`? " ,
2024-07-24 19:37:13 -04:00
package_json_path . display ( ) ,
2024-03-05 19:23:51 -05:00
) ) ;
}
}
2024-07-23 20:22:24 -04:00
Err ( err )
2024-03-05 19:23:51 -05:00
}
}
2024-01-27 12:10:36 -05:00
}
2024-03-05 19:23:51 -05:00
pub fn resolve_package_sub_path_from_deno_module (
2024-01-27 12:10:36 -05:00
& self ,
package_folder : & Path ,
sub_path : Option < & str > ,
2024-07-09 12:15:03 -04:00
maybe_referrer : Option < & ModuleSpecifier > ,
2024-03-05 19:23:51 -05:00
mode : NodeResolutionMode ,
2024-07-03 20:54:33 -04:00
) -> Result < NodeResolution , AnyError > {
2024-07-23 20:22:24 -04:00
let res = self
2024-07-16 18:32:41 -04:00
. node_resolver
. resolve_package_subpath_from_deno_module (
package_folder ,
sub_path ,
maybe_referrer ,
mode ,
) ? ;
2024-07-23 20:22:24 -04:00
Ok ( self . handle_node_resolution ( res ) )
2024-01-27 12:10:36 -05:00
}
2024-03-05 19:23:51 -05:00
pub fn handle_if_in_node_modules (
& self ,
2024-07-25 09:07:59 -04:00
specifier : & ModuleSpecifier ,
) -> Result < Option < ModuleSpecifier > , AnyError > {
2024-03-05 19:23:51 -05:00
// skip canonicalizing if we definitely know it's unnecessary
if specifier . scheme ( ) = = " file "
& & specifier . path ( ) . contains ( " /node_modules/ " )
{
// Specifiers in the node_modules directory are canonicalized
// so canoncalize then check if it's in the node_modules directory.
// If so, check if we need to store this specifier as being a CJS
// resolution.
let specifier =
2024-07-25 09:07:59 -04:00
crate ::node ::resolve_specifier_into_node_modules ( specifier ) ;
2024-03-05 19:23:51 -05:00
if self . in_npm_package ( & specifier ) {
2024-07-10 14:46:25 -04:00
let resolution =
self . node_resolver . url_to_node_resolution ( specifier ) ? ;
2024-10-14 20:48:39 -04:00
let resolution = self . handle_node_resolution ( resolution ) ;
2024-07-25 09:07:59 -04:00
return Ok ( Some ( resolution . into_url ( ) ) ) ;
2024-03-05 19:23:51 -05:00
}
}
2024-07-25 09:07:59 -04:00
Ok ( None )
2024-03-05 19:23:51 -05:00
}
2024-04-15 17:50:52 -04:00
pub fn url_to_node_resolution (
& self ,
specifier : ModuleSpecifier ,
2024-07-09 12:15:03 -04:00
) -> Result < NodeResolution , UrlToNodeResolutionError > {
2024-04-15 17:50:52 -04:00
self . node_resolver . url_to_node_resolution ( specifier )
}
2024-07-16 18:32:41 -04:00
fn handle_node_resolution (
2024-01-27 12:10:36 -05:00
& self ,
2024-07-23 20:22:24 -04:00
resolution : NodeResolution ,
) -> NodeResolution {
if let NodeResolution ::CommonJs ( specifier ) = & resolution {
2024-07-16 18:32:41 -04:00
// remember that this was a common js resolution
2024-10-14 20:48:39 -04:00
self . mark_cjs_resolution ( specifier . clone ( ) ) ;
2024-01-27 12:10:36 -05:00
}
2024-07-23 20:22:24 -04:00
resolution
2024-01-27 12:10:36 -05:00
}
2024-10-14 20:48:39 -04:00
pub fn mark_cjs_resolution ( & self , specifier : ModuleSpecifier ) {
self . cjs_resolutions . insert ( specifier ) ;
}
2024-01-27 12:10:36 -05:00
}
2024-10-14 20:48:39 -04:00
// todo(dsherret): move to module_loader.rs
2024-05-16 03:09:35 -04:00
#[ derive(Clone) ]
2024-01-27 12:10:36 -05:00
pub struct NpmModuleLoader {
cjs_resolutions : Arc < CjsResolutionStore > ,
node_code_translator : Arc < CliNodeCodeTranslator > ,
fs : Arc < dyn deno_fs ::FileSystem > ,
node_resolver : Arc < CliNodeResolver > ,
}
impl NpmModuleLoader {
pub fn new (
cjs_resolutions : Arc < CjsResolutionStore > ,
node_code_translator : Arc < CliNodeCodeTranslator > ,
fs : Arc < dyn deno_fs ::FileSystem > ,
node_resolver : Arc < CliNodeResolver > ,
) -> Self {
Self {
cjs_resolutions ,
node_code_translator ,
fs ,
node_resolver ,
}
}
2024-10-14 20:48:39 -04:00
pub fn if_in_npm_package ( & self , specifier : & ModuleSpecifier ) -> bool {
self . node_resolver . in_npm_package ( specifier )
| | self . cjs_resolutions . is_known_cjs ( specifier )
2024-01-27 12:10:36 -05:00
}
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-10-14 20:48:39 -04:00
let code = if self . cjs_resolutions . is_known_cjs ( specifier ) {
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-06-06 23:37:53 -04:00
. translate_cjs_to_esm ( specifier , Some ( code ) )
2024-06-05 11:04:16 -04:00
. await ?
. 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
} ;
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 ) ,
} )
}
}
/// Keeps track of what module specifiers were resolved as CJS.
#[ derive(Debug, Default) ]
2024-05-18 11:42:03 -04:00
pub struct CjsResolutionStore ( DashSet < ModuleSpecifier > ) ;
2024-01-27 12:10:36 -05:00
impl CjsResolutionStore {
2024-10-14 20:48:39 -04:00
pub fn is_known_cjs ( & self , specifier : & ModuleSpecifier ) -> bool {
if specifier . scheme ( ) ! = " file " {
return false ;
}
specifier_has_extension ( specifier , " cjs " ) | | self . 0. contains ( specifier )
2024-01-27 12:10:36 -05:00
}
pub fn insert ( & self , specifier : ModuleSpecifier ) {
2024-05-18 11:42:03 -04:00
self . 0. insert ( specifier ) ;
2024-01-27 12:10:36 -05:00
}
}
2024-09-28 19:17:48 -04:00
pub type CliSloppyImportsResolver =
SloppyImportsResolver < SloppyImportsCachedFs > ;
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) ]
2023-02-15 11:30:54 -05:00
pub struct CliGraphResolver {
2024-07-03 20:54:33 -04:00
node_resolver : Option < Arc < CliNodeResolver > > ,
npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
2024-09-28 19:17:48 -04:00
sloppy_imports_resolver : Option < Arc < CliSloppyImportsResolver > > ,
2024-07-03 20:54:33 -04:00
workspace_resolver : Arc < WorkspaceResolver > ,
2022-11-02 10:47:02 -04:00
maybe_default_jsx_import_source : Option < String > ,
2024-04-30 14:12:35 -04:00
maybe_default_jsx_import_source_types : Option < String > ,
2022-11-02 10:47:02 -04:00
maybe_jsx_import_source_module : Option < String > ,
2023-08-17 12:14:22 -04:00
maybe_vendor_specifier : Option < ModuleSpecifier > ,
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
}
2023-08-17 12:14:22 -04:00
pub struct CliGraphResolverOptions < ' a > {
2024-03-05 19:23:51 -05:00
pub node_resolver : Option < Arc < CliNodeResolver > > ,
2023-10-25 14:39:00 -04:00
pub npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
2024-09-28 19:17:48 -04:00
pub sloppy_imports_resolver : Option < Arc < CliSloppyImportsResolver > > ,
2024-07-03 20:54:33 -04:00
pub workspace_resolver : Arc < WorkspaceResolver > ,
pub bare_node_builtins_enabled : bool ,
2023-08-17 12:14:22 -04:00
pub maybe_jsx_import_source_config : Option < JsxImportSourceConfig > ,
pub maybe_vendor_dir : Option < & ' a PathBuf > ,
}
2023-02-15 11:30:54 -05:00
impl CliGraphResolver {
2023-10-25 14:39:00 -04:00
pub fn new ( options : CliGraphResolverOptions ) -> Self {
2023-02-15 11:30:54 -05:00
Self {
2024-07-03 20:54:33 -04:00
node_resolver : options . node_resolver ,
npm_resolver : options . npm_resolver ,
2023-12-06 19:03:18 -05:00
sloppy_imports_resolver : options . sloppy_imports_resolver ,
2024-07-03 20:54:33 -04:00
workspace_resolver : options . workspace_resolver ,
2023-08-17 12:14:22 -04:00
maybe_default_jsx_import_source : options
. maybe_jsx_import_source_config
2023-02-15 11:30:54 -05:00
. as_ref ( )
. and_then ( | c | c . default_specifier . clone ( ) ) ,
2024-04-30 14:12:35 -04:00
maybe_default_jsx_import_source_types : options
. maybe_jsx_import_source_config
. as_ref ( )
. and_then ( | c | c . default_types_specifier . clone ( ) ) ,
2023-08-17 12:14:22 -04:00
maybe_jsx_import_source_module : options
. maybe_jsx_import_source_config
2023-02-15 11:30:54 -05:00
. map ( | c | c . module ) ,
2023-08-17 12:14:22 -04:00
maybe_vendor_specifier : options
. maybe_vendor_dir
. and_then ( | v | ModuleSpecifier ::from_directory_path ( v ) . ok ( ) ) ,
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
2022-11-02 10:47:02 -04:00
pub fn as_graph_resolver ( & self ) -> & dyn Resolver {
2021-11-08 20:26:39 -05:00
self
}
2023-02-22 14:15:25 -05:00
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
}
2021-11-08 20:26:39 -05:00
}
2023-02-15 11:30:54 -05:00
impl Resolver for CliGraphResolver {
2022-08-24 13:36:05 -04:00
fn default_jsx_import_source ( & self ) -> Option < String > {
2022-11-02 10:47:02 -04:00
self . maybe_default_jsx_import_source . clone ( )
2022-08-24 13:36:05 -04:00
}
2024-04-30 14:12:35 -04:00
fn default_jsx_import_source_types ( & self ) -> Option < String > {
self . maybe_default_jsx_import_source_types . clone ( )
}
2021-11-08 20:26:39 -05:00
fn jsx_import_source_module ( & self ) -> & str {
2022-11-02 10:47:02 -04:00
self
. maybe_jsx_import_source_module
. as_deref ( )
. unwrap_or ( DEFAULT_JSX_IMPORT_SOURCE_MODULE )
2021-11-08 20:26:39 -05:00
}
fn resolve (
& self ,
2024-09-04 10:00:44 -04:00
raw_specifier : & str ,
2023-12-06 19:03:18 -05:00
referrer_range : & deno_graph ::Range ,
2023-10-25 14:39:00 -04:00
mode : ResolutionMode ,
2023-10-20 00:02:08 -04:00
) -> Result < ModuleSpecifier , ResolveError > {
2023-10-25 14:39:00 -04:00
fn to_node_mode ( mode : ResolutionMode ) -> NodeResolutionMode {
match mode {
ResolutionMode ::Execution = > NodeResolutionMode ::Execution ,
ResolutionMode ::Types = > NodeResolutionMode ::Types ,
}
}
2023-12-06 19:03:18 -05:00
let referrer = & referrer_range . specifier ;
2024-07-23 20:22:24 -04:00
// Use node resolution if we're in an npm package
if let Some ( node_resolver ) = self . node_resolver . as_ref ( ) {
if referrer . scheme ( ) = = " file " & & node_resolver . in_npm_package ( referrer ) {
return node_resolver
2024-09-04 10:00:44 -04:00
. resolve ( raw_specifier , referrer , to_node_mode ( mode ) )
2024-07-23 20:22:24 -04:00
. map ( | res | res . into_url ( ) )
. map_err ( | e | ResolveError ::Other ( e . into ( ) ) ) ;
}
}
// Attempt to resolve with the workspace resolver
2024-03-05 19:23:51 -05:00
let result : Result < _ , ResolveError > = self
2024-07-03 20:54:33 -04:00
. workspace_resolver
2024-09-04 10:00:44 -04:00
. resolve ( raw_specifier , referrer )
2024-07-03 20:54:33 -04:00
. map_err ( | err | match err {
MappedResolutionError ::Specifier ( err ) = > ResolveError ::Specifier ( err ) ,
MappedResolutionError ::ImportMap ( err ) = > {
ResolveError ::Other ( err . into ( ) )
}
2024-08-07 03:43:05 -04:00
MappedResolutionError ::Workspace ( err ) = > {
ResolveError ::Other ( err . into ( ) )
}
2024-07-03 20:54:33 -04:00
} ) ;
let result = match result {
Ok ( resolution ) = > match resolution {
2024-08-20 10:11:43 -04:00
MappedResolution ::Normal {
specifier ,
maybe_diagnostic ,
}
| MappedResolution ::ImportMap {
specifier ,
maybe_diagnostic ,
} = > {
if let Some ( diagnostic ) = maybe_diagnostic {
match & * diagnostic {
MappedResolutionDiagnostic ::ConstraintNotMatchedLocalVersion { reference , .. } = > {
if self . warned_pkgs . insert ( reference . req ( ) . clone ( ) ) {
log ::warn! ( " {} {} \n at {} " , colors ::yellow ( " Warning " ) , diagnostic , referrer_range ) ;
}
}
}
}
2024-07-23 20:22:24 -04:00
// do sloppy imports resolution if enabled
if let Some ( sloppy_imports_resolver ) = & self . sloppy_imports_resolver {
2024-07-25 09:07:59 -04:00
Ok (
sloppy_imports_resolver
2024-09-28 19:17:48 -04:00
. resolve (
& specifier ,
match mode {
ResolutionMode ::Execution = > {
SloppyImportsResolutionMode ::Execution
}
ResolutionMode ::Types = > SloppyImportsResolutionMode ::Types ,
} ,
)
2024-07-25 09:07:59 -04:00
. map ( | s | s . into_specifier ( ) )
. unwrap_or ( specifier ) ,
)
2024-07-23 20:22:24 -04:00
} else {
Ok ( specifier )
}
}
2024-08-07 03:43:05 -04:00
MappedResolution ::WorkspaceJsrPackage { specifier , .. } = > {
Ok ( specifier )
}
2024-07-17 09:13:22 -04:00
MappedResolution ::WorkspaceNpmPackage {
target_pkg_json : pkg_json ,
sub_path ,
..
} = > self
. node_resolver
. as_ref ( )
. unwrap ( )
. resolve_package_sub_path_from_deno_module (
pkg_json . dir_path ( ) ,
sub_path . as_deref ( ) ,
Some ( referrer ) ,
to_node_mode ( mode ) ,
)
. map_err ( ResolveError ::Other )
. map ( | res | res . into_url ( ) ) ,
2024-07-03 20:54:33 -04:00
MappedResolution ::PackageJson {
dep_result ,
alias ,
sub_path ,
..
} = > {
2024-01-03 22:50:58 -05:00
// found a specifier in the package.json, so mark that
// we need to do an "npm install" later
self . found_package_json_dep_flag . raise ( ) ;
2024-07-03 20:54:33 -04:00
dep_result
. as_ref ( )
. map_err ( | e | ResolveError ::Other ( e . clone ( ) . into ( ) ) )
. and_then ( | dep | match dep {
PackageJsonDepValue ::Req ( req ) = > {
ModuleSpecifier ::parse ( & format! (
" npm:{}{} " ,
req ,
sub_path . map ( | s | format! ( " / {} " , s ) ) . unwrap_or_default ( )
) )
. map_err ( | e | ResolveError ::Other ( e . into ( ) ) )
}
PackageJsonDepValue ::Workspace ( version_req ) = > self
. workspace_resolver
. resolve_workspace_pkg_json_folder_for_pkg_json_dep (
alias ,
version_req ,
)
. map_err ( | e | ResolveError ::Other ( e . into ( ) ) )
. and_then ( | pkg_folder | {
Ok (
self
. node_resolver
. as_ref ( )
. unwrap ( )
. resolve_package_sub_path_from_deno_module (
pkg_folder ,
sub_path . as_deref ( ) ,
2024-07-09 12:15:03 -04:00
Some ( referrer ) ,
2024-07-03 20:54:33 -04:00
to_node_mode ( mode ) ,
) ?
. into_url ( ) ,
)
} ) ,
} )
2024-01-03 22:50:58 -05:00
}
2024-07-03 20:54:33 -04:00
} ,
Err ( err ) = > Err ( err ) ,
} ;
2024-07-23 20:22:24 -04:00
// When the user is vendoring, don't allow them to import directly from the vendor/ directory
// as it might cause them confusion or duplicate dependencies. Additionally, this folder has
// special treatment in the language server so it will definitely cause issues/confusion there
// if they do this.
if let Some ( vendor_specifier ) = & self . maybe_vendor_specifier {
2024-07-03 20:54:33 -04:00
if let Ok ( specifier ) = & result {
2024-07-23 20:22:24 -04:00
if specifier . as_str ( ) . starts_with ( vendor_specifier . as_str ( ) ) {
return Err ( ResolveError ::Other ( anyhow! ( " Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring. " ) ) ) ;
}
}
}
let Some ( node_resolver ) = & self . node_resolver else {
return result ;
} ;
let is_byonm = self
. npm_resolver
. as_ref ( )
. is_some_and ( | r | r . as_byonm ( ) . is_some ( ) ) ;
match result {
Ok ( specifier ) = > {
if let Ok ( npm_req_ref ) =
NpmPackageReqReference ::from_specifier ( & specifier )
{
// check if the npm specifier resolves to a workspace member
2024-07-03 20:54:33 -04:00
if let Some ( pkg_folder ) = self
. workspace_resolver
2024-07-23 20:22:24 -04:00
. resolve_workspace_pkg_json_folder_for_npm_specifier (
npm_req_ref . req ( ) ,
)
2024-07-03 20:54:33 -04:00
{
return Ok (
node_resolver
. resolve_package_sub_path_from_deno_module (
pkg_folder ,
2024-07-23 20:22:24 -04:00
npm_req_ref . sub_path ( ) ,
2024-07-09 12:15:03 -04:00
Some ( referrer ) ,
2024-07-03 20:54:33 -04:00
to_node_mode ( mode ) ,
) ?
. into_url ( ) ,
) ;
}
2023-08-17 12:14:22 -04:00
2024-07-23 20:22:24 -04:00
// do npm resolution for byonm
if is_byonm {
2024-03-05 19:23:51 -05:00
return node_resolver
2024-06-06 23:37:53 -04:00
. resolve_req_reference ( & npm_req_ref , referrer , to_node_mode ( mode ) )
2024-03-05 19:23:51 -05:00
. map ( | res | res . into_url ( ) )
. map_err ( | err | err . into ( ) ) ;
2023-10-25 14:39:00 -04:00
}
}
2024-07-23 20:22:24 -04:00
2024-07-25 09:07:59 -04:00
Ok ( match node_resolver . handle_if_in_node_modules ( & specifier ) ? {
Some ( specifier ) = > specifier ,
None = > specifier ,
} )
2024-07-23 20:22:24 -04:00
}
Err ( err ) = > {
// If byonm, check if the bare specifier resolves to an npm package
if is_byonm & & referrer . scheme ( ) = = " file " {
let maybe_resolution = node_resolver
2024-09-04 10:00:44 -04:00
. resolve_if_for_npm_pkg ( raw_specifier , referrer , to_node_mode ( mode ) )
2024-07-23 20:22:24 -04:00
. map_err ( ResolveError ::Other ) ? ;
if let Some ( res ) = maybe_resolution {
2024-09-09 17:35:41 -04:00
match res {
NodeResolution ::Esm ( url ) | NodeResolution ::CommonJs ( url ) = > {
return Ok ( url )
}
NodeResolution ::BuiltIn ( _ ) = > {
// don't resolve bare specifiers for built-in modules via node resolution
}
}
2023-10-25 14:39:00 -04:00
}
}
2024-07-09 09:25:50 -04:00
2024-07-23 20:22:24 -04:00
Err ( err )
2024-05-21 12:54:15 -04:00
}
2023-10-25 14:39:00 -04:00
}
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 ,
) {
let deno_graph ::Range {
start , specifier , ..
} = range ;
let line = start . line + 1 ;
let column = start . character + 1 ;
2024-03-07 08:59:57 -05:00
if ! * DENO_DISABLE_PEDANTIC_NODE_WARNINGS {
2024-09-03 04:45:01 -04:00
log ::warn! ( " {} Resolving \" {module_name} \" as \" node:{module_name} \" at {specifier}:{line}:{column}. 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
}
}