2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2021-10-10 17:26:22 -04:00
2023-03-03 17:27:05 -05:00
use deno_core ::anyhow ::anyhow ;
2023-01-24 08:23:19 -05:00
use deno_core ::error ::AnyError ;
2023-02-22 14:15:25 -05:00
use deno_core ::futures ::future ;
use deno_core ::futures ::future ::LocalBoxFuture ;
use deno_core ::futures ::FutureExt ;
2021-10-10 17:26:22 -04:00
use deno_core ::ModuleSpecifier ;
2023-09-07 09:09:16 -04:00
use deno_graph ::source ::NpmPackageReqResolution ;
2023-02-22 14:15:25 -05:00
use deno_graph ::source ::NpmResolver ;
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 ;
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-26 21:22:15 -04:00
use deno_runtime ::deno_node ::parse_npm_pkg_name ;
2023-10-25 14:39:00 -04:00
use deno_runtime ::deno_node ::NodeResolution ;
use deno_runtime ::deno_node ::NodeResolutionMode ;
use deno_runtime ::deno_node ::NodeResolver ;
2023-10-26 21:22:15 -04:00
use deno_runtime ::deno_node ::NpmResolver as DenoNodeNpmResolver ;
2023-10-25 14:39:00 -04:00
use deno_runtime ::permissions ::PermissionsContainer ;
use deno_semver ::npm ::NpmPackageReqReference ;
2023-08-21 05:53:52 -04:00
use deno_semver ::package ::PackageReq ;
2021-10-10 17:26:22 -04:00
use import_map ::ImportMap ;
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
2023-03-03 17:27:05 -05:00
use crate ::args ::package_json ::PackageJsonDeps ;
2022-11-25 17:00:28 -05:00
use crate ::args ::JsxImportSourceConfig ;
2023-05-10 20:06:59 -04:00
use crate ::args ::PackageJsonDepsProvider ;
2023-10-25 14:39:00 -04:00
use crate ::module_loader ::CjsResolutionStore ;
2023-10-26 21:22:15 -04:00
use crate ::npm ::ByonmCliNpmResolver ;
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 ;
2022-08-24 13:36:05 -04:00
2023-05-10 20:06:59 -04:00
/// Result of checking if a specifier is mapped via
/// an import map or package.json.
pub enum MappedResolution {
None ,
PackageJson ( ModuleSpecifier ) ,
ImportMap ( ModuleSpecifier ) ,
}
impl MappedResolution {
pub fn into_specifier ( self ) -> Option < ModuleSpecifier > {
match self {
MappedResolution ::None = > Option ::None ,
MappedResolution ::PackageJson ( specifier ) = > Some ( specifier ) ,
MappedResolution ::ImportMap ( specifier ) = > Some ( specifier ) ,
}
}
}
/// Resolver for specifiers that could be mapped via an
/// import map or package.json.
#[ derive(Debug) ]
pub struct MappedSpecifierResolver {
maybe_import_map : Option < Arc < ImportMap > > ,
package_json_deps_provider : Arc < PackageJsonDepsProvider > ,
}
impl MappedSpecifierResolver {
pub fn new (
maybe_import_map : Option < Arc < ImportMap > > ,
package_json_deps_provider : Arc < PackageJsonDepsProvider > ,
) -> Self {
Self {
maybe_import_map ,
package_json_deps_provider ,
}
}
pub fn resolve (
& self ,
specifier : & str ,
referrer : & ModuleSpecifier ,
) -> Result < MappedResolution , AnyError > {
// attempt to resolve with the import map first
let maybe_import_map_err = match self
. maybe_import_map
. as_ref ( )
. map ( | import_map | import_map . resolve ( specifier , referrer ) )
{
Some ( Ok ( value ) ) = > return Ok ( MappedResolution ::ImportMap ( value ) ) ,
Some ( Err ( err ) ) = > Some ( err ) ,
None = > None ,
} ;
// then with package.json
if let Some ( deps ) = self . package_json_deps_provider . deps ( ) {
if let Some ( specifier ) = resolve_package_json_dep ( specifier , deps ) ? {
return Ok ( MappedResolution ::PackageJson ( specifier ) ) ;
}
}
// otherwise, surface the import map error or try resolving when has no import map
if let Some ( err ) = maybe_import_map_err {
Err ( err . into ( ) )
} else {
Ok ( MappedResolution ::None )
}
}
}
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 {
2023-10-25 14:39:00 -04:00
fs : Arc < dyn FileSystem > ,
2023-05-10 20:06:59 -04:00
mapped_specifier_resolver : MappedSpecifierResolver ,
2022-11-02 10:47:02 -04:00
maybe_default_jsx_import_source : Option < String > ,
maybe_jsx_import_source_module : Option < String > ,
2023-08-17 12:14:22 -04:00
maybe_vendor_specifier : Option < ModuleSpecifier > ,
2023-10-25 14:39:00 -04:00
cjs_resolutions : Option < Arc < CjsResolutionStore > > ,
node_resolver : Option < Arc < NodeResolver > > ,
2023-09-30 12:06:38 -04:00
npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
2023-04-11 18:10:51 -04:00
found_package_json_dep_flag : Arc < AtomicFlag > ,
2023-10-20 00:02:08 -04:00
bare_node_builtins_enabled : bool ,
2023-02-22 14:15:25 -05:00
}
2023-08-17 12:14:22 -04:00
pub struct CliGraphResolverOptions < ' a > {
2023-10-25 14:39:00 -04:00
pub fs : Arc < dyn FileSystem > ,
pub cjs_resolutions : Option < Arc < CjsResolutionStore > > ,
pub node_resolver : Option < Arc < NodeResolver > > ,
pub npm_resolver : Option < Arc < dyn CliNpmResolver > > ,
pub package_json_deps_provider : Arc < PackageJsonDepsProvider > ,
2023-08-17 12:14:22 -04:00
pub maybe_jsx_import_source_config : Option < JsxImportSourceConfig > ,
pub maybe_import_map : Option < Arc < ImportMap > > ,
pub maybe_vendor_dir : Option < & ' a PathBuf > ,
2023-10-20 00:02:08 -04:00
pub bare_node_builtins_enabled : bool ,
2023-08-17 12:14:22 -04:00
}
2023-02-15 11:30:54 -05:00
impl CliGraphResolver {
2023-10-25 14:39:00 -04:00
pub fn new ( options : CliGraphResolverOptions ) -> Self {
let is_byonm = options
. npm_resolver
. as_ref ( )
. map ( | n | n . as_byonm ( ) . is_some ( ) )
. unwrap_or ( false ) ;
2023-02-15 11:30:54 -05:00
Self {
2023-10-25 14:39:00 -04:00
fs : options . fs ,
cjs_resolutions : options . cjs_resolutions ,
2023-10-03 19:05:06 -04:00
mapped_specifier_resolver : MappedSpecifierResolver ::new (
options . maybe_import_map ,
2023-10-25 14:39:00 -04:00
if is_byonm {
// don't resolve from the root package.json deps for byonm
Arc ::new ( PackageJsonDepsProvider ::new ( None ) )
} else {
options . package_json_deps_provider
} ,
2023-10-03 19:05:06 -04:00
) ,
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 ( ) ) ,
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-10-25 14:39:00 -04:00
node_resolver : options . node_resolver ,
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 ,
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
pub fn as_graph_npm_resolver ( & self ) -> & dyn NpmResolver {
self
}
2023-04-11 18:10:51 -04:00
2023-09-30 12:06:38 -04:00
pub fn found_package_json_dep ( & self ) -> bool {
self . found_package_json_dep_flag . is_raised ( )
2023-04-11 18:10:51 -04:00
}
2023-10-26 21:22:15 -04:00
fn check_surface_byonm_node_error (
& self ,
specifier : & str ,
referrer : & ModuleSpecifier ,
mode : NodeResolutionMode ,
original_err : AnyError ,
resolver : & ByonmCliNpmResolver ,
) -> Result < ( ) , AnyError > {
if let Ok ( ( pkg_name , _ , _ ) ) = parse_npm_pkg_name ( specifier , referrer ) {
match resolver
. resolve_package_folder_from_package ( & pkg_name , referrer , mode )
{
Ok ( _ ) = > {
return Err ( original_err ) ;
}
Err ( _ ) = > {
if resolver
. find_ancestor_package_json_with_dep ( & pkg_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. " ,
" Did you forget to run `npm install`? "
) ,
specifier
) ) ;
}
}
}
}
Ok ( ( ) )
}
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
}
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 ,
specifier : & str ,
referrer : & ModuleSpecifier ,
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-08-17 12:14:22 -04:00
let result = match self
2023-05-10 20:06:59 -04:00
. mapped_specifier_resolver
. resolve ( specifier , referrer ) ?
2023-02-24 19:35:43 -05:00
{
2023-10-03 19:05:06 -04:00
MappedResolution ::ImportMap ( specifier ) = > Ok ( specifier ) ,
MappedResolution ::PackageJson ( specifier ) = > {
2023-05-10 20:06:59 -04:00
// found a specifier in the package.json, so mark that
// we need to do an "npm install" later
2023-04-11 18:10:51 -04:00
self . found_package_json_dep_flag . raise ( ) ;
2023-05-10 20:06:59 -04:00
Ok ( specifier )
2023-02-23 12:33:23 -05:00
}
2023-10-03 19:05:06 -04:00
MappedResolution ::None = > deno_graph ::resolve_import ( specifier , referrer )
2023-05-10 20:06:59 -04:00
. map_err ( | err | err . into ( ) ) ,
2023-08-17 12:14:22 -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 {
if let Ok ( specifier ) = & result {
if specifier . as_str ( ) . starts_with ( vendor_specifier . as_str ( ) ) {
2023-10-20 00:02:08 -04:00
return Err ( ResolveError ::Other ( anyhow! ( " Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring. " ) ) ) ;
2023-08-17 12:14:22 -04:00
}
}
2023-02-23 17:20:23 -05:00
}
2023-08-17 12:14:22 -04:00
2023-10-25 14:39:00 -04:00
if let Some ( resolver ) =
self . npm_resolver . as_ref ( ) . and_then ( | r | r . as_byonm ( ) )
{
match & result {
Ok ( specifier ) = > {
if let Ok ( npm_req_ref ) =
NpmPackageReqReference ::from_specifier ( specifier )
{
let package_folder = resolver
. resolve_pkg_folder_from_deno_module_req (
npm_req_ref . req ( ) ,
referrer ,
) ? ;
let node_resolver = self . node_resolver . as_ref ( ) . unwrap ( ) ;
let package_json_path = package_folder . join ( " package.json " ) ;
if ! self . fs . exists_sync ( & package_json_path ) {
return Err ( ResolveError ::Other ( anyhow! (
2023-10-26 21:22:15 -04:00
" Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `npm install`? " ,
2023-10-25 14:39:00 -04:00
package_json_path . display ( )
) ) ) ;
}
let maybe_resolution = node_resolver
. resolve_package_subpath_from_deno_module (
& package_folder ,
npm_req_ref . sub_path ( ) ,
referrer ,
to_node_mode ( mode ) ,
& PermissionsContainer ::allow_all ( ) ,
) ? ;
match maybe_resolution {
Some ( resolution ) = > {
if let Some ( cjs_resolutions ) = & self . cjs_resolutions {
if let NodeResolution ::CommonJs ( specifier ) = & resolution {
// remember that this was a common js resolution
cjs_resolutions . insert ( specifier . clone ( ) ) ;
}
}
return Ok ( resolution . into_url ( ) ) ;
}
None = > {
return Err ( ResolveError ::Other ( anyhow! (
" Failed resolving package subpath for '{}' in '{}'. " ,
npm_req_ref ,
package_folder . display ( )
) ) ) ;
}
}
}
}
Err ( _ ) = > {
if referrer . scheme ( ) = = " file " {
if let Some ( node_resolver ) = & self . node_resolver {
let node_result = node_resolver . resolve (
specifier ,
referrer ,
to_node_mode ( mode ) ,
& PermissionsContainer ::allow_all ( ) ,
) ;
2023-10-26 21:22:15 -04:00
match node_result {
Ok ( Some ( resolution ) ) = > {
if let Some ( cjs_resolutions ) = & self . cjs_resolutions {
if let NodeResolution ::CommonJs ( specifier ) = & resolution {
// remember that this was a common js resolution
cjs_resolutions . insert ( specifier . clone ( ) ) ;
}
2023-10-25 14:39:00 -04:00
}
2023-10-26 21:22:15 -04:00
return Ok ( resolution . into_url ( ) ) ;
}
Ok ( None ) = > {
self
. check_surface_byonm_node_error (
specifier ,
referrer ,
to_node_mode ( mode ) ,
anyhow! ( " Cannot find \" {} \" " , specifier ) ,
resolver ,
)
. map_err ( ResolveError ::Other ) ? ;
}
Err ( err ) = > {
self
. check_surface_byonm_node_error (
specifier ,
referrer ,
to_node_mode ( mode ) ,
err ,
resolver ,
)
. map_err ( ResolveError ::Other ) ? ;
2023-10-25 14:39:00 -04:00
}
}
}
}
}
}
}
2023-08-17 12:14:22 -04:00
result
2021-11-08 20:26:39 -05:00
}
}
2023-02-22 14:15:25 -05:00
2023-02-23 12:33:23 -05:00
fn resolve_package_json_dep (
specifier : & str ,
2023-03-03 17:27:05 -05:00
deps : & PackageJsonDeps ,
) -> Result < Option < ModuleSpecifier > , AnyError > {
for ( bare_specifier , req_result ) in deps {
2023-02-23 12:33:23 -05:00
if specifier . starts_with ( bare_specifier ) {
let path = & specifier [ bare_specifier . len ( ) .. ] ;
2023-03-03 17:27:05 -05:00
if path . is_empty ( ) | | path . starts_with ( '/' ) {
let req = req_result . as_ref ( ) . map_err ( | err | {
anyhow! (
" Parsing version constraints in the application-level package.json is more strict at the moment. \n \n {:#} " ,
err . clone ( )
)
} ) ? ;
return Ok ( Some ( ModuleSpecifier ::parse ( & format! ( " npm: {req} {path} " ) ) ? ) ) ;
2023-02-23 12:33:23 -05:00
}
}
}
Ok ( None )
}
2023-02-22 14:15:25 -05:00
impl NpmResolver for CliGraphResolver {
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 ;
log ::warn! ( " Warning: 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. " )
}
2023-02-22 14:15:25 -05:00
fn load_and_cache_npm_package_info (
& self ,
package_name : & str ,
2023-04-12 08:36:11 -04:00
) -> LocalBoxFuture < 'static , Result < ( ) , AnyError > > {
2023-09-30 12:06:38 -04:00
match & self . npm_resolver {
Some ( npm_resolver ) if npm_resolver . as_managed ( ) . is_some ( ) = > {
let package_name = package_name . to_string ( ) ;
let npm_resolver = npm_resolver . clone ( ) ;
async move {
if let Some ( managed ) = npm_resolver . as_managed ( ) {
managed . cache_package_info ( & package_name ) . await ? ;
}
Ok ( ( ) )
}
. boxed ( )
}
_ = > {
// return it succeeded and error at the import site below
Box ::pin ( future ::ready ( Ok ( ( ) ) ) )
}
2023-02-22 14:15:25 -05:00
}
}
2023-09-07 09:09:16 -04:00
fn resolve_npm ( & self , package_req : & PackageReq ) -> NpmPackageReqResolution {
2023-09-30 12:06:38 -04:00
match & self . npm_resolver {
2023-10-03 19:05:06 -04:00
Some ( npm_resolver ) = > match npm_resolver . as_inner ( ) {
InnerCliNpmResolverRef ::Managed ( npm_resolver ) = > {
npm_resolver . resolve_npm_for_deno_graph ( package_req )
}
// 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! ( ) ,
} ,
2023-09-30 12:06:38 -04:00
None = > NpmPackageReqResolution ::Err ( anyhow! (
" npm specifiers were requested; but --no-npm is specified "
) ) ,
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
#[ cfg(test) ]
mod test {
2023-03-03 17:27:05 -05:00
use std ::collections ::BTreeMap ;
2023-02-23 12:33:23 -05:00
use super ::* ;
#[ test ]
fn test_resolve_package_json_dep ( ) {
fn resolve (
specifier : & str ,
2023-08-21 05:53:52 -04:00
deps : & BTreeMap < String , PackageReq > ,
2023-02-23 12:33:23 -05:00
) -> Result < Option < String > , String > {
2023-03-03 17:27:05 -05:00
let deps = deps
. iter ( )
. map ( | ( key , value ) | ( key . to_string ( ) , Ok ( value . clone ( ) ) ) )
. collect ( ) ;
resolve_package_json_dep ( specifier , & deps )
2023-02-23 12:33:23 -05:00
. map ( | s | s . map ( | s | s . to_string ( ) ) )
. map_err ( | err | err . to_string ( ) )
}
let deps = BTreeMap ::from ( [
(
" package " . to_string ( ) ,
2023-08-21 05:53:52 -04:00
PackageReq ::from_str ( " package@1.0 " ) . unwrap ( ) ,
2023-02-23 12:33:23 -05:00
) ,
(
" package-alias " . to_string ( ) ,
2023-08-21 05:53:52 -04:00
PackageReq ::from_str ( " package@^1.2 " ) . unwrap ( ) ,
2023-02-23 12:33:23 -05:00
) ,
(
" @deno/test " . to_string ( ) ,
2023-08-21 05:53:52 -04:00
PackageReq ::from_str ( " @deno/test@~0.2 " ) . unwrap ( ) ,
2023-02-23 12:33:23 -05:00
) ,
] ) ;
assert_eq! (
resolve ( " package " , & deps ) . unwrap ( ) ,
Some ( " npm:package@1.0 " . to_string ( ) ) ,
) ;
assert_eq! (
resolve ( " package/some_path.ts " , & deps ) . unwrap ( ) ,
2023-03-03 17:27:05 -05:00
Some ( " npm:package@1.0/some_path.ts " . to_string ( ) ) ,
2023-02-23 12:33:23 -05:00
) ;
assert_eq! (
resolve ( " @deno/test " , & deps ) . unwrap ( ) ,
Some ( " npm:@deno/test@~0.2 " . to_string ( ) ) ,
) ;
assert_eq! (
resolve ( " @deno/test/some_path.ts " , & deps ) . unwrap ( ) ,
2023-03-03 17:27:05 -05:00
Some ( " npm:@deno/test@~0.2/some_path.ts " . to_string ( ) ) ,
2023-02-23 12:33:23 -05:00
) ;
// matches the start, but doesn't have the same length or a path
assert_eq! ( resolve ( " @deno/testing " , & deps ) . unwrap ( ) , None , ) ;
// alias
assert_eq! (
resolve ( " package-alias " , & deps ) . unwrap ( ) ,
Some ( " npm:package@^1.2 " . to_string ( ) ) ,
) ;
// non-existent bare specifier
assert_eq! ( resolve ( " non-existent " , & deps ) . unwrap ( ) , None ) ;
}
}