From a615eb3b56545960ec9684991442dd34a8b2abfc Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Apr 2023 21:02:46 -0400 Subject: refactor(node): move most of cli/node to ext/node (#18797) This is just a straight refactor and I didn't do any cleanup in ext/node. After this PR we can start to clean it up and make things private that don't need to be public anymore. --- cli/Cargo.toml | 4 +- cli/graph_util.rs | 9 +- cli/lsp/diagnostics.rs | 8 +- cli/lsp/documents.rs | 10 +- cli/lsp/language_server.rs | 22 +- cli/module_loader.rs | 26 +- cli/node.rs | 206 +++++++++++++ cli/node/analyze.rs | 178 ----------- cli/node/mod.rs | 735 --------------------------------------------- cli/npm/mod.rs | 3 +- cli/npm/resolution.rs | 2 +- cli/npm/resolvers/mod.rs | 91 +++--- cli/ops/mod.rs | 8 +- cli/proc_state.rs | 17 +- cli/tools/check.rs | 6 +- cli/tools/info.rs | 10 +- cli/tools/task.rs | 8 +- cli/tsc/mod.rs | 16 +- cli/worker.rs | 23 +- 19 files changed, 332 insertions(+), 1050 deletions(-) create mode 100644 cli/node.rs delete mode 100644 cli/node/analyze.rs delete mode 100644 cli/node/mod.rs (limited to 'cli') diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 64ce5fce7..96fe458ae 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -47,9 +47,9 @@ deno_emit = "0.20.0" deno_graph = "=0.48.0" deno_lint = { version = "0.44.0", features = ["docs"] } deno_lockfile.workspace = true -deno_npm = "0.3.0" +deno_npm.workspace = true deno_runtime = { workspace = true, features = ["dont_create_runtime_snapshot", "include_js_files_for_snapshotting"] } -deno_semver = "0.2.1" +deno_semver.workspace = true deno_task_shell = "0.11.0" napi_sym.workspace = true diff --git a/cli/graph_util.rs b/cli/graph_util.rs index dacd3be17..d5bc6ac0d 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -9,7 +9,7 @@ use crate::cache::ParsedSourceCache; use crate::colors; use crate::errors::get_error_class_name; use crate::file_fetcher::FileFetcher; -use crate::npm::NpmPackageResolver; +use crate::npm::CliNpmResolver; use crate::resolver::CliGraphResolver; use crate::tools::check; use crate::tools::check::TypeChecker; @@ -29,6 +29,7 @@ use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; use deno_graph::ResolutionError; use deno_graph::SpecifierError; +use deno_runtime::deno_node; use deno_runtime::permissions::PermissionsContainer; use import_map::ImportMapError; use std::collections::HashMap; @@ -165,7 +166,7 @@ pub fn graph_lock_or_exit(graph: &ModuleGraph, lockfile: &mut Lockfile) { pub struct ModuleGraphBuilder { options: Arc, resolver: Arc, - npm_resolver: Arc, + npm_resolver: Arc, parsed_source_cache: Arc, lockfile: Option>>, emit_cache: cache::EmitCache, @@ -178,7 +179,7 @@ impl ModuleGraphBuilder { pub fn new( options: Arc, resolver: Arc, - npm_resolver: Arc, + npm_resolver: Arc, parsed_source_cache: Arc, lockfile: Option>>, emit_cache: cache::EmitCache, @@ -377,7 +378,7 @@ pub fn get_resolution_error_bare_node_specifier( error: &ResolutionError, ) -> Option<&str> { get_resolution_error_bare_specifier(error).filter(|specifier| { - crate::node::resolve_builtin_node_module(specifier).is_ok() + deno_node::resolve_builtin_node_module(specifier).is_ok() }) } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 8c2126561..965075a2d 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -16,7 +16,6 @@ use super::tsc::TsServer; use crate::args::LintOptions; use crate::graph_util; use crate::graph_util::enhanced_resolution_error_message; -use crate::node; use crate::tools::lint::get_configured_rules; use deno_ast::MediaType; @@ -31,6 +30,7 @@ use deno_graph::Resolution; use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_lint::rules::LintRule; +use deno_runtime::deno_node; use deno_runtime::tokio_util::create_basic_runtime; use deno_semver::npm::NpmPackageReqReference; use log::error; @@ -469,8 +469,8 @@ async fn generate_lint_diagnostics( } // ignore any npm package files - if let Some(npm_resolver) = &snapshot.maybe_npm_resolver { - if npm_resolver.in_npm_package(document.specifier()) { + if let Some(node_resolver) = &snapshot.maybe_node_resolver { + if node_resolver.in_npm_package(document.specifier()) { continue; } } @@ -926,7 +926,7 @@ fn diagnose_resolution( } } else if let Some(module_name) = specifier.as_str().strip_prefix("node:") { - if node::resolve_builtin_node_module(module_name).is_err() { + if deno_node::resolve_builtin_node_module(module_name).is_err() { diagnostics.push( DenoDiagnostic::InvalidNodeSpecifier(specifier.clone()) .to_lsp_diagnostic(&range), diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 7b206406c..fd40bb95f 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -18,7 +18,6 @@ use crate::file_fetcher::map_content_type; use crate::file_fetcher::SUPPORTED_SCHEMES; use crate::lsp::logging::lsp_warn; use crate::node::CliNodeResolver; -use crate::node::NodeResolution; use crate::npm::CliNpmRegistryApi; use crate::npm::NpmResolution; use crate::npm::PackageJsonDepsInstaller; @@ -37,8 +36,11 @@ use deno_core::url; use deno_core::ModuleSpecifier; use deno_graph::GraphImport; use deno_graph::Resolution; +use deno_runtime::deno_node; +use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::PackageJson; +use deno_runtime::deno_node::RealFs; use deno_runtime::permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReq; use deno_semver::npm::NpmPackageReqReference; @@ -1069,7 +1071,7 @@ impl Documents { // we're in an npm package, so use node resolution results.push(Some(NodeResolution::into_specifier_and_media_type( node_resolver - .resolve( + .resolve::( &specifier, referrer, NodeResolutionMode::Types, @@ -1082,7 +1084,7 @@ impl Documents { } } if let Some(module_name) = specifier.strip_prefix("node:") { - if crate::node::resolve_builtin_node_module(module_name).is_ok() { + if deno_node::resolve_builtin_node_module(module_name).is_ok() { // return itself for node: specifiers because during type checking // we resolve to the ambient modules in the @types/node package // rather than deno_std/node @@ -1457,7 +1459,7 @@ fn node_resolve_npm_req_ref( maybe_node_resolver.map(|node_resolver| { NodeResolution::into_specifier_and_media_type( node_resolver - .resolve_npm_req_reference( + .resolve_npm_req_reference::( &npm_req_ref, NodeResolutionMode::Types, &mut PermissionsContainer::allow_all(), diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 239ff8a6e..f1b9cb434 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -9,6 +9,7 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; +use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_web::BlobStore; use import_map::ImportMap; @@ -81,8 +82,8 @@ use crate::lsp::urls::LspUrlKind; use crate::node::CliNodeResolver; use crate::npm::create_npm_fs_resolver; use crate::npm::CliNpmRegistryApi; +use crate::npm::CliNpmResolver; use crate::npm::NpmCache; -use crate::npm::NpmPackageResolver; use crate::npm::NpmResolution; use crate::proc_state::ProcState; use crate::tools::fmt::format_file; @@ -103,7 +104,7 @@ pub struct StateSnapshot { pub documents: Documents, pub maybe_import_map: Option>, pub maybe_node_resolver: Option>, - pub maybe_npm_resolver: Option>, + pub maybe_npm_resolver: Option>, } #[derive(Debug)] @@ -153,7 +154,7 @@ pub struct Inner { /// Npm resolution that is stored in memory. npm_resolution: Arc, /// Resolver for npm packages. - npm_resolver: Arc, + npm_resolver: Arc, /// A collection of measurements which instrument that performance of the LSP. performance: Arc, /// A memoized version of fixable diagnostic codes retrieved from TypeScript. @@ -424,7 +425,7 @@ fn create_lsp_structs( ) -> ( Arc, Arc, - Arc, + Arc, Arc, ) { let registry_url = CliNpmRegistryApi::default_url(); @@ -457,11 +458,7 @@ fn create_lsp_structs( ( api, npm_cache, - Arc::new(NpmPackageResolver::new( - resolution.clone(), - fs_resolver, - None, - )), + Arc::new(CliNpmResolver::new(resolution.clone(), fs_resolver, None)), resolution, ) } @@ -703,19 +700,18 @@ impl Inner { self.npm_resolution.snapshot(), None, )); - let npm_resolver = Arc::new(NpmPackageResolver::new( + let npm_resolver = Arc::new(CliNpmResolver::new( npm_resolution.clone(), create_npm_fs_resolver( self.npm_cache.clone(), &ProgressBar::new(ProgressBarStyle::TextOnly), self.npm_api.base_url().clone(), - npm_resolution.clone(), + npm_resolution, None, ), None, )); - let node_resolver = - Arc::new(CliNodeResolver::new(npm_resolution, npm_resolver.clone())); + let node_resolver = Arc::new(NodeResolver::new(npm_resolver.clone())); Arc::new(StateSnapshot { assets: self.assets.snapshot(), cache_metadata: self.cache_metadata.clone(), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 07fad6ffc..c4ef0ed7e 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -11,10 +11,8 @@ use crate::graph_util::graph_valid_with_cli_options; use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphContainer; use crate::node; -use crate::node::CliCjsEsmCodeAnalyzer; +use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; -use crate::node::NodeResolution; -use crate::npm::CliRequireNpmResolver; use crate::proc_state::CjsResolutionStore; use crate::proc_state::FileWatcherReporter; use crate::proc_state::ProcState; @@ -50,7 +48,8 @@ use deno_graph::JsonModule; use deno_graph::Module; use deno_graph::Resolution; use deno_lockfile::Lockfile; -use deno_runtime::deno_node::analyze::NodeCodeTranslator; +use deno_runtime::deno_node; +use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::RealFs; use deno_runtime::permissions::PermissionsContainer; @@ -244,8 +243,7 @@ pub struct CliModuleLoader { emitter: Arc, graph_container: Arc, module_load_preparer: Arc, - node_code_translator: - Arc>, + node_code_translator: Arc, node_resolver: Arc, parsed_source_cache: Arc, resolver: Arc, @@ -430,7 +428,7 @@ impl CliModuleLoader { fn handle_node_resolve_result( &self, - result: Result, AnyError>, + result: Result, AnyError>, ) -> Result { let response = match result? { Some(response) => response, @@ -440,7 +438,7 @@ impl CliModuleLoader { // remember that this was a common js resolution self.cjs_resolutions.insert(specifier.clone()); } else if let NodeResolution::BuiltIn(specifier) = &response { - return node::resolve_builtin_node_module(specifier); + return deno_node::resolve_builtin_node_module(specifier); } Ok(response.into_url()) } @@ -468,7 +466,7 @@ impl ModuleLoader for CliModuleLoader { if self.node_resolver.in_npm_package(referrer) { // we're in an npm package, so use node resolution return self - .handle_node_resolve_result(self.node_resolver.resolve( + .handle_node_resolve_result(self.node_resolver.resolve::( specifier, referrer, NodeResolutionMode::Execution, @@ -494,7 +492,7 @@ impl ModuleLoader for CliModuleLoader { return match graph.get(specifier) { Some(Module::Npm(module)) => self .handle_node_resolve_result( - self.node_resolver.resolve_npm_reference( + self.node_resolver.resolve_npm_reference::( &module.nv_reference, NodeResolutionMode::Execution, &mut permissions, @@ -504,7 +502,7 @@ impl ModuleLoader for CliModuleLoader { format!("Could not resolve '{}'.", module.nv_reference) }), Some(Module::Node(module)) => { - node::resolve_builtin_node_module(&module.module_name) + deno_node::resolve_builtin_node_module(&module.module_name) } Some(Module::Esm(module)) => Ok(module.specifier.clone()), Some(Module::Json(module)) => Ok(module.specifier.clone()), @@ -526,7 +524,7 @@ impl ModuleLoader for CliModuleLoader { // Built-in Node modules if let Some(module_name) = specifier.strip_prefix("node:") { - return node::resolve_builtin_node_module(module_name); + return deno_node::resolve_builtin_node_module(module_name); } // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL @@ -556,9 +554,9 @@ impl ModuleLoader for CliModuleLoader { { return self .handle_node_resolve_result( - self.node_resolver.resolve_npm_req_reference( + self.node_resolver.resolve_npm_req_reference::( &reference, - deno_runtime::deno_node::NodeResolutionMode::Execution, + NodeResolutionMode::Execution, &mut permissions, ), ) diff --git a/cli/node.rs b/cli/node.rs new file mode 100644 index 000000000..3ec9500e8 --- /dev/null +++ b/cli/node.rs @@ -0,0 +1,206 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use std::collections::HashSet; +use std::sync::Arc; + +use deno_ast::swc::common::SyntaxContext; +use deno_ast::view::Node; +use deno_ast::view::NodeTrait; +use deno_ast::CjsAnalysis; +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_ast::ParsedSource; +use deno_ast::SourceRanged; +use deno_core::error::AnyError; +use deno_runtime::deno_node::analyze::CjsAnalysis as ExtNodeCjsAnalysis; +use deno_runtime::deno_node::analyze::CjsEsmCodeAnalyzer; +use deno_runtime::deno_node::analyze::NodeCodeTranslator; +use deno_runtime::deno_node::NodeResolver; + +use crate::cache::NodeAnalysisCache; +use crate::npm::CliNpmResolver; +use crate::util::fs::canonicalize_path_maybe_not_exists; + +pub type CliNodeCodeTranslator = + NodeCodeTranslator>; +pub type CliNodeResolver = NodeResolver>; + +/// Resolves a specifier that is pointing into a node_modules folder. +/// +/// Note: This should be called whenever getting the specifier from +/// a Module::External(module) reference because that module might +/// not be fully resolved at the time deno_graph is analyzing it +/// because the node_modules folder might not exist at that time. +pub fn resolve_specifier_into_node_modules( + specifier: &ModuleSpecifier, +) -> ModuleSpecifier { + specifier + .to_file_path() + .ok() + // this path might not exist at the time the graph is being created + // because the node_modules folder might not yet exist + .and_then(|path| canonicalize_path_maybe_not_exists(&path).ok()) + .and_then(|path| ModuleSpecifier::from_file_path(path).ok()) + .unwrap_or_else(|| specifier.clone()) +} + +pub struct CliCjsEsmCodeAnalyzer { + cache: NodeAnalysisCache, +} + +impl CliCjsEsmCodeAnalyzer { + pub fn new(cache: NodeAnalysisCache) -> Self { + Self { cache } + } + + fn inner_cjs_analysis( + &self, + specifier: &ModuleSpecifier, + source: &str, + ) -> Result { + let source_hash = NodeAnalysisCache::compute_source_hash(source); + if let Some(analysis) = self + .cache + .get_cjs_analysis(specifier.as_str(), &source_hash) + { + return Ok(analysis); + } + + let media_type = MediaType::from_specifier(specifier); + if media_type == MediaType::Json { + return Ok(CjsAnalysis { + exports: vec![], + reexports: vec![], + }); + } + + let parsed_source = deno_ast::parse_script(deno_ast::ParseParams { + specifier: specifier.to_string(), + text_info: deno_ast::SourceTextInfo::new(source.into()), + media_type, + capture_tokens: true, + scope_analysis: false, + maybe_syntax: None, + })?; + let analysis = parsed_source.analyze_cjs(); + self + .cache + .set_cjs_analysis(specifier.as_str(), &source_hash, &analysis); + + Ok(analysis) + } +} + +impl CjsEsmCodeAnalyzer for CliCjsEsmCodeAnalyzer { + fn analyze_cjs( + &self, + specifier: &ModuleSpecifier, + source: &str, + ) -> Result { + let analysis = self.inner_cjs_analysis(specifier, source)?; + Ok(ExtNodeCjsAnalysis { + exports: analysis.exports, + reexports: analysis.reexports, + }) + } + + fn analyze_esm_top_level_decls( + &self, + specifier: &ModuleSpecifier, + source: &str, + ) -> Result, AnyError> { + // TODO(dsherret): this code is way more inefficient than it needs to be. + // + // In the future, we should disable capturing tokens & scope analysis + // and instead only use swc's APIs to go through the portions of the tree + // that we know will affect the global scope while still ensuring that + // `var` decls are taken into consideration. + let source_hash = NodeAnalysisCache::compute_source_hash(source); + if let Some(decls) = self + .cache + .get_esm_analysis(specifier.as_str(), &source_hash) + { + Ok(HashSet::from_iter(decls)) + } else { + let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { + specifier: specifier.to_string(), + text_info: deno_ast::SourceTextInfo::from_string(source.to_string()), + media_type: deno_ast::MediaType::from_specifier(specifier), + capture_tokens: true, + scope_analysis: true, + maybe_syntax: None, + })?; + let top_level_decls = analyze_top_level_decls(&parsed_source)?; + self.cache.set_esm_analysis( + specifier.as_str(), + &source_hash, + &top_level_decls.clone().into_iter().collect::>(), + ); + Ok(top_level_decls) + } + } +} + +fn analyze_top_level_decls( + parsed_source: &ParsedSource, +) -> Result, AnyError> { + fn visit_children( + node: Node, + top_level_context: SyntaxContext, + results: &mut HashSet, + ) { + if let Node::Ident(ident) = node { + if ident.ctxt() == top_level_context && is_local_declaration_ident(node) { + results.insert(ident.sym().to_string()); + } + } + + for child in node.children() { + visit_children(child, top_level_context, results); + } + } + + let top_level_context = parsed_source.top_level_context(); + + parsed_source.with_view(|program| { + let mut results = HashSet::new(); + visit_children(program.into(), top_level_context, &mut results); + Ok(results) + }) +} + +fn is_local_declaration_ident(node: Node) -> bool { + if let Some(parent) = node.parent() { + match parent { + Node::BindingIdent(decl) => decl.id.range().contains(&node.range()), + Node::ClassDecl(decl) => decl.ident.range().contains(&node.range()), + Node::ClassExpr(decl) => decl + .ident + .as_ref() + .map(|i| i.range().contains(&node.range())) + .unwrap_or(false), + Node::TsInterfaceDecl(decl) => decl.id.range().contains(&node.range()), + Node::FnDecl(decl) => decl.ident.range().contains(&node.range()), + Node::FnExpr(decl) => decl + .ident + .as_ref() + .map(|i| i.range().contains(&node.range())) + .unwrap_or(false), + Node::TsModuleDecl(decl) => decl.id.range().contains(&node.range()), + Node::TsNamespaceDecl(decl) => decl.id.range().contains(&node.range()), + Node::VarDeclarator(decl) => decl.name.range().contains(&node.range()), + Node::ImportNamedSpecifier(decl) => { + decl.local.range().contains(&node.range()) + } + Node::ImportDefaultSpecifier(decl) => { + decl.local.range().contains(&node.range()) + } + Node::ImportStarAsSpecifier(decl) => decl.range().contains(&node.range()), + Node::KeyValuePatProp(decl) => decl.key.range().contains(&node.range()), + Node::AssignPatProp(decl) => decl.key.range().contains(&node.range()), + _ => false, + } + } else { + false + } +} diff --git a/cli/node/analyze.rs b/cli/node/analyze.rs deleted file mode 100644 index 27818639e..000000000 --- a/cli/node/analyze.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::collections::HashSet; - -use deno_ast::swc::common::SyntaxContext; -use deno_ast::view::Node; -use deno_ast::view::NodeTrait; -use deno_ast::CjsAnalysis; -use deno_ast::MediaType; -use deno_ast::ModuleSpecifier; -use deno_ast::ParsedSource; -use deno_ast::SourceRanged; -use deno_core::error::AnyError; -use deno_runtime::deno_node::analyze::CjsAnalysis as ExtNodeCjsAnalysis; -use deno_runtime::deno_node::analyze::CjsEsmCodeAnalyzer; - -use crate::cache::NodeAnalysisCache; - -pub struct CliCjsEsmCodeAnalyzer { - cache: NodeAnalysisCache, -} - -impl CliCjsEsmCodeAnalyzer { - pub fn new(cache: NodeAnalysisCache) -> Self { - Self { cache } - } - - fn inner_cjs_analysis( - &self, - specifier: &ModuleSpecifier, - source: &str, - ) -> Result { - let source_hash = NodeAnalysisCache::compute_source_hash(source); - if let Some(analysis) = self - .cache - .get_cjs_analysis(specifier.as_str(), &source_hash) - { - return Ok(analysis); - } - - let media_type = MediaType::from_specifier(specifier); - if media_type == MediaType::Json { - return Ok(CjsAnalysis { - exports: vec![], - reexports: vec![], - }); - } - - let parsed_source = deno_ast::parse_script(deno_ast::ParseParams { - specifier: specifier.to_string(), - text_info: deno_ast::SourceTextInfo::new(source.into()), - media_type, - capture_tokens: true, - scope_analysis: false, - maybe_syntax: None, - })?; - let analysis = parsed_source.analyze_cjs(); - self - .cache - .set_cjs_analysis(specifier.as_str(), &source_hash, &analysis); - - Ok(analysis) - } -} - -impl CjsEsmCodeAnalyzer for CliCjsEsmCodeAnalyzer { - fn analyze_cjs( - &self, - specifier: &ModuleSpecifier, - source: &str, - ) -> Result { - let analysis = self.inner_cjs_analysis(specifier, source)?; - Ok(ExtNodeCjsAnalysis { - exports: analysis.exports, - reexports: analysis.reexports, - }) - } - - fn analyze_esm_top_level_decls( - &self, - specifier: &ModuleSpecifier, - source: &str, - ) -> Result, AnyError> { - // TODO(dsherret): this code is way more inefficient than it needs to be. - // - // In the future, we should disable capturing tokens & scope analysis - // and instead only use swc's APIs to go through the portions of the tree - // that we know will affect the global scope while still ensuring that - // `var` decls are taken into consideration. - let source_hash = NodeAnalysisCache::compute_source_hash(source); - if let Some(decls) = self - .cache - .get_esm_analysis(specifier.as_str(), &source_hash) - { - Ok(HashSet::from_iter(decls)) - } else { - let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { - specifier: specifier.to_string(), - text_info: deno_ast::SourceTextInfo::from_string(source.to_string()), - media_type: deno_ast::MediaType::from_specifier(specifier), - capture_tokens: true, - scope_analysis: true, - maybe_syntax: None, - })?; - let top_level_decls = analyze_top_level_decls(&parsed_source)?; - self.cache.set_esm_analysis( - specifier.as_str(), - &source_hash, - &top_level_decls.clone().into_iter().collect::>(), - ); - Ok(top_level_decls) - } - } -} - -fn analyze_top_level_decls( - parsed_source: &ParsedSource, -) -> Result, AnyError> { - fn visit_children( - node: Node, - top_level_context: SyntaxContext, - results: &mut HashSet, - ) { - if let Node::Ident(ident) = node { - if ident.ctxt() == top_level_context && is_local_declaration_ident(node) { - results.insert(ident.sym().to_string()); - } - } - - for child in node.children() { - visit_children(child, top_level_context, results); - } - } - - let top_level_context = parsed_source.top_level_context(); - - parsed_source.with_view(|program| { - let mut results = HashSet::new(); - visit_children(program.into(), top_level_context, &mut results); - Ok(results) - }) -} - -fn is_local_declaration_ident(node: Node) -> bool { - if let Some(parent) = node.parent() { - match parent { - Node::BindingIdent(decl) => decl.id.range().contains(&node.range()), - Node::ClassDecl(decl) => decl.ident.range().contains(&node.range()), - Node::ClassExpr(decl) => decl - .ident - .as_ref() - .map(|i| i.range().contains(&node.range())) - .unwrap_or(false), - Node::TsInterfaceDecl(decl) => decl.id.range().contains(&node.range()), - Node::FnDecl(decl) => decl.ident.range().contains(&node.range()), - Node::FnExpr(decl) => decl - .ident - .as_ref() - .map(|i| i.range().contains(&node.range())) - .unwrap_or(false), - Node::TsModuleDecl(decl) => decl.id.range().contains(&node.range()), - Node::TsNamespaceDecl(decl) => decl.id.range().contains(&node.range()), - Node::VarDeclarator(decl) => decl.name.range().contains(&node.range()), - Node::ImportNamedSpecifier(decl) => { - decl.local.range().contains(&node.range()) - } - Node::ImportDefaultSpecifier(decl) => { - decl.local.range().contains(&node.range()) - } - Node::ImportStarAsSpecifier(decl) => decl.range().contains(&node.range()), - Node::KeyValuePatProp(decl) => decl.key.range().contains(&node.range()), - Node::AssignPatProp(decl) => decl.key.range().contains(&node.range()), - _ => false, - } - } else { - false - } -} diff --git a/cli/node/mod.rs b/cli/node/mod.rs deleted file mode 100644 index eb584879e..000000000 --- a/cli/node/mod.rs +++ /dev/null @@ -1,735 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; - -use deno_ast::MediaType; -use deno_ast::ModuleSpecifier; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::AnyError; -use deno_core::serde_json::Value; -use deno_core::url::Url; -use deno_runtime::deno_node; -use deno_runtime::deno_node::errors; -use deno_runtime::deno_node::find_builtin_node_module; -use deno_runtime::deno_node::get_closest_package_json; -use deno_runtime::deno_node::legacy_main_resolve; -use deno_runtime::deno_node::package_exports_resolve; -use deno_runtime::deno_node::package_imports_resolve; -use deno_runtime::deno_node::package_resolve; -use deno_runtime::deno_node::path_to_declaration_path; -use deno_runtime::deno_node::NodeModuleKind; -use deno_runtime::deno_node::NodePermissions; -use deno_runtime::deno_node::NodeResolutionMode; -use deno_runtime::deno_node::PackageJson; -use deno_runtime::deno_node::RealFs; -use deno_runtime::deno_node::RequireNpmResolver; -use deno_runtime::deno_node::DEFAULT_CONDITIONS; -use deno_runtime::permissions::PermissionsContainer; -use deno_semver::npm::NpmPackageNv; -use deno_semver::npm::NpmPackageNvReference; -use deno_semver::npm::NpmPackageReqReference; - -use crate::npm::CliRequireNpmResolver; -use crate::npm::NpmPackageResolver; -use crate::npm::NpmResolution; -use crate::util::fs::canonicalize_path_maybe_not_exists; - -mod analyze; - -pub use analyze::CliCjsEsmCodeAnalyzer; - -#[derive(Debug)] -pub enum NodeResolution { - Esm(ModuleSpecifier), - CommonJs(ModuleSpecifier), - BuiltIn(String), -} - -impl NodeResolution { - pub fn into_url(self) -> ModuleSpecifier { - match self { - Self::Esm(u) => u, - Self::CommonJs(u) => u, - Self::BuiltIn(specifier) => { - if specifier.starts_with("node:") { - ModuleSpecifier::parse(&specifier).unwrap() - } else { - ModuleSpecifier::parse(&format!("node:{specifier}")).unwrap() - } - } - } - } - - pub fn into_specifier_and_media_type( - resolution: Option, - ) -> (ModuleSpecifier, MediaType) { - match resolution { - Some(NodeResolution::CommonJs(specifier)) => { - let media_type = MediaType::from_specifier(&specifier); - ( - specifier, - match media_type { - MediaType::JavaScript | MediaType::Jsx => MediaType::Cjs, - MediaType::TypeScript | MediaType::Tsx => MediaType::Cts, - MediaType::Dts => MediaType::Dcts, - _ => media_type, - }, - ) - } - Some(NodeResolution::Esm(specifier)) => { - let media_type = MediaType::from_specifier(&specifier); - ( - specifier, - match media_type { - MediaType::JavaScript | MediaType::Jsx => MediaType::Mjs, - MediaType::TypeScript | MediaType::Tsx => MediaType::Mts, - MediaType::Dts => MediaType::Dmts, - _ => media_type, - }, - ) - } - Some(resolution) => (resolution.into_url(), MediaType::Dts), - None => ( - ModuleSpecifier::parse("internal:///missing_dependency.d.ts").unwrap(), - MediaType::Dts, - ), - } - } -} - -// TODO(bartlomieju): seems super wasteful to parse specified each time -pub fn resolve_builtin_node_module(module_name: &str) -> Result { - if let Some(module) = find_builtin_node_module(module_name) { - return Ok(ModuleSpecifier::parse(module.specifier).unwrap()); - } - - Err(generic_error(format!( - "Unknown built-in \"node:\" module: {module_name}" - ))) -} - -#[derive(Debug)] -pub struct CliNodeResolver { - npm_resolution: Arc, - npm_resolver: Arc, - require_npm_resolver: CliRequireNpmResolver, -} - -impl CliNodeResolver { - pub fn new( - npm_resolution: Arc, - npm_package_resolver: Arc, - ) -> Self { - Self { - npm_resolution, - require_npm_resolver: npm_package_resolver.as_require_npm_resolver(), - npm_resolver: npm_package_resolver, - } - } - - pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { - self.npm_resolver.in_npm_package(specifier) - } - - /// This function is an implementation of `defaultResolve` in - /// `lib/internal/modules/esm/resolve.js` from Node. - pub fn resolve( - &self, - specifier: &str, - referrer: &ModuleSpecifier, - mode: NodeResolutionMode, - permissions: &mut dyn NodePermissions, - ) -> Result, AnyError> { - // Note: if we are here, then the referrer is an esm module - // TODO(bartlomieju): skipped "policy" part as we don't plan to support it - - if deno_node::is_builtin_node_module(specifier) { - return Ok(Some(NodeResolution::BuiltIn(specifier.to_string()))); - } - - if let Ok(url) = Url::parse(specifier) { - if url.scheme() == "data" { - return Ok(Some(NodeResolution::Esm(url))); - } - - let protocol = url.scheme(); - - if protocol == "node" { - let split_specifier = url.as_str().split(':'); - let specifier = split_specifier.skip(1).collect::(); - - if deno_node::is_builtin_node_module(&specifier) { - return Ok(Some(NodeResolution::BuiltIn(specifier))); - } - } - - if protocol != "file" && protocol != "data" { - return Err(errors::err_unsupported_esm_url_scheme(&url)); - } - - // todo(dsherret): this seems wrong - if referrer.scheme() == "data" { - let url = referrer.join(specifier).map_err(AnyError::from)?; - return Ok(Some(NodeResolution::Esm(url))); - } - } - - let url = self.module_resolve( - specifier, - referrer, - DEFAULT_CONDITIONS, - mode, - permissions, - )?; - let url = match url { - Some(url) => url, - None => return Ok(None), - }; - let url = match mode { - NodeResolutionMode::Execution => url, - NodeResolutionMode::Types => { - let path = url.to_file_path().unwrap(); - // todo(16370): the module kind is not correct here. I think we need - // typescript to tell us if the referrer is esm or cjs - let path = - match path_to_declaration_path::(path, NodeModuleKind::Esm) { - Some(path) => path, - None => return Ok(None), - }; - ModuleSpecifier::from_file_path(path).unwrap() - } - }; - - let resolve_response = self.url_to_node_resolution(url)?; - // TODO(bartlomieju): skipped checking errors for commonJS resolution and - // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(Some(resolve_response)) - } - - fn module_resolve( - &self, - specifier: &str, - referrer: &ModuleSpecifier, - conditions: &[&str], - mode: NodeResolutionMode, - permissions: &mut dyn NodePermissions, - ) -> Result, AnyError> { - // note: if we're here, the referrer is an esm module - let url = if should_be_treated_as_relative_or_absolute_path(specifier) { - let resolved_specifier = referrer.join(specifier)?; - if mode.is_types() { - let file_path = to_file_path(&resolved_specifier); - // todo(dsherret): the node module kind is not correct and we - // should use the value provided by typescript instead - let declaration_path = - path_to_declaration_path::(file_path, NodeModuleKind::Esm); - declaration_path.map(|declaration_path| { - ModuleSpecifier::from_file_path(declaration_path).unwrap() - }) - } else { - Some(resolved_specifier) - } - } else if specifier.starts_with('#') { - Some( - package_imports_resolve::( - specifier, - referrer, - NodeModuleKind::Esm, - conditions, - mode, - &self.require_npm_resolver, - permissions, - ) - .map(|p| ModuleSpecifier::from_file_path(p).unwrap())?, - ) - } else if let Ok(resolved) = Url::parse(specifier) { - Some(resolved) - } else { - package_resolve::( - specifier, - referrer, - NodeModuleKind::Esm, - conditions, - mode, - &self.require_npm_resolver, - permissions, - )? - .map(|p| ModuleSpecifier::from_file_path(p).unwrap()) - }; - Ok(match url { - Some(url) => Some(finalize_resolution(url, referrer)?), - None => None, - }) - } - - pub fn resolve_npm_req_reference( - &self, - reference: &NpmPackageReqReference, - mode: NodeResolutionMode, - permissions: &mut dyn NodePermissions, - ) -> Result, AnyError> { - let reference = self.npm_resolution.pkg_req_ref_to_nv_ref(reference)?; - self.resolve_npm_reference(&reference, mode, permissions) - } - - pub fn resolve_npm_reference( - &self, - reference: &NpmPackageNvReference, - mode: NodeResolutionMode, - permissions: &mut dyn NodePermissions, - ) -> Result, AnyError> { - let package_folder = self - .npm_resolver - .resolve_package_folder_from_deno_module(&reference.nv)?; - let node_module_kind = NodeModuleKind::Esm; - let maybe_resolved_path = package_config_resolve( - &reference - .sub_path - .as_ref() - .map(|s| format!("./{s}")) - .unwrap_or_else(|| ".".to_string()), - &package_folder, - node_module_kind, - DEFAULT_CONDITIONS, - mode, - &self.require_npm_resolver, - permissions, - ) - .with_context(|| { - format!("Error resolving package config for '{reference}'") - })?; - let resolved_path = match maybe_resolved_path { - Some(resolved_path) => resolved_path, - None => return Ok(None), - }; - let resolved_path = match mode { - NodeResolutionMode::Execution => resolved_path, - NodeResolutionMode::Types => { - match path_to_declaration_path::( - resolved_path, - node_module_kind, - ) { - Some(path) => path, - None => return Ok(None), - } - } - }; - let url = ModuleSpecifier::from_file_path(resolved_path).unwrap(); - let resolve_response = self.url_to_node_resolution(url)?; - // TODO(bartlomieju): skipped checking errors for commonJS resolution and - // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(Some(resolve_response)) - } - - pub fn resolve_binary_commands( - &self, - pkg_nv: &NpmPackageNv, - ) -> Result, AnyError> { - let package_folder = self - .npm_resolver - .resolve_package_folder_from_deno_module(pkg_nv)?; - let package_json_path = package_folder.join("package.json"); - let package_json = PackageJson::load::( - &self.require_npm_resolver, - &mut PermissionsContainer::allow_all(), - package_json_path, - )?; - - Ok(match package_json.bin { - Some(Value::String(_)) => vec![pkg_nv.name.to_string()], - Some(Value::Object(o)) => { - o.into_iter().map(|(key, _)| key).collect::>() - } - _ => Vec::new(), - }) - } - - pub fn resolve_binary_export( - &self, - pkg_ref: &NpmPackageReqReference, - ) -> Result { - let pkg_nv = self - .npm_resolution - .resolve_pkg_id_from_pkg_req(&pkg_ref.req)? - .nv; - let bin_name = pkg_ref.sub_path.as_deref(); - let package_folder = self - .npm_resolver - .resolve_package_folder_from_deno_module(&pkg_nv)?; - let package_json_path = package_folder.join("package.json"); - let package_json = PackageJson::load::( - &self.require_npm_resolver, - &mut PermissionsContainer::allow_all(), - package_json_path, - )?; - let bin = match &package_json.bin { - Some(bin) => bin, - None => bail!( - "package '{}' did not have a bin property in its package.json", - &pkg_nv.name, - ), - }; - let bin_entry = resolve_bin_entry_value(&pkg_nv, bin_name, bin)?; - let url = - ModuleSpecifier::from_file_path(package_folder.join(bin_entry)).unwrap(); - - let resolve_response = self.url_to_node_resolution(url)?; - // TODO(bartlomieju): skipped checking errors for commonJS resolution and - // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(resolve_response) - } - - pub fn url_to_node_resolution( - &self, - url: ModuleSpecifier, - ) -> Result { - let url_str = url.as_str().to_lowercase(); - if url_str.starts_with("http") { - Ok(NodeResolution::Esm(url)) - } else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") { - let package_config = get_closest_package_json::( - &url, - &self.require_npm_resolver, - &mut PermissionsContainer::allow_all(), - )?; - if package_config.typ == "module" { - Ok(NodeResolution::Esm(url)) - } else { - Ok(NodeResolution::CommonJs(url)) - } - } else if url_str.ends_with(".mjs") || url_str.ends_with(".d.mts") { - Ok(NodeResolution::Esm(url)) - } else if url_str.ends_with(".ts") { - Err(generic_error(format!( - "TypeScript files are not supported in npm packages: {url}" - ))) - } else { - Ok(NodeResolution::CommonJs(url)) - } - } -} - -/// Resolves a specifier that is pointing into a node_modules folder. -/// -/// Note: This should be called whenever getting the specifier from -/// a Module::External(module) reference because that module might -/// not be fully resolved at the time deno_graph is analyzing it -/// because the node_modules folder might not exist at that time. -pub fn resolve_specifier_into_node_modules( - specifier: &ModuleSpecifier, -) -> ModuleSpecifier { - specifier - .to_file_path() - .ok() - // this path might not exist at the time the graph is being created - // because the node_modules folder might not yet exist - .and_then(|path| canonicalize_path_maybe_not_exists(&path).ok()) - .and_then(|path| ModuleSpecifier::from_file_path(path).ok()) - .unwrap_or_else(|| specifier.clone()) -} - -fn resolve_bin_entry_value<'a>( - pkg_nv: &NpmPackageNv, - bin_name: Option<&str>, - bin: &'a Value, -) -> Result<&'a str, AnyError> { - let bin_entry = match bin { - Value::String(_) => { - if bin_name.is_some() && bin_name.unwrap() != pkg_nv.name { - None - } else { - Some(bin) - } - } - Value::Object(o) => { - if let Some(bin_name) = bin_name { - o.get(bin_name) - } else if o.len() == 1 || o.len() > 1 && o.values().all(|v| v == o.values().next().unwrap()) { - o.values().next() - } else { - o.get(&pkg_nv.name) - } - }, - _ => bail!("package '{}' did not have a bin property with a string or object value in its package.json", pkg_nv), - }; - let bin_entry = match bin_entry { - Some(e) => e, - None => { - let keys = bin - .as_object() - .map(|o| { - o.keys() - .map(|k| format!(" * npm:{pkg_nv}/{k}")) - .collect::>() - }) - .unwrap_or_default(); - bail!( - "package '{}' did not have a bin entry for '{}' in its package.json{}", - pkg_nv, - bin_name.unwrap_or(&pkg_nv.name), - if keys.is_empty() { - "".to_string() - } else { - format!("\n\nPossibilities:\n{}", keys.join("\n")) - } - ) - } - }; - match bin_entry { - Value::String(s) => Ok(s), - _ => bail!( - "package '{}' had a non-string sub property of bin in its package.json", - pkg_nv, - ), - } -} - -fn package_config_resolve( - package_subpath: &str, - package_dir: &Path, - referrer_kind: NodeModuleKind, - conditions: &[&str], - mode: NodeResolutionMode, - npm_resolver: &dyn RequireNpmResolver, - permissions: &mut dyn NodePermissions, -) -> Result, AnyError> { - let package_json_path = package_dir.join("package.json"); - let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap(); - let package_config = PackageJson::load::( - npm_resolver, - permissions, - package_json_path.clone(), - )?; - if let Some(exports) = &package_config.exports { - let result = package_exports_resolve::( - &package_json_path, - package_subpath.to_string(), - exports, - &referrer, - referrer_kind, - conditions, - mode, - npm_resolver, - permissions, - ); - match result { - Ok(found) => return Ok(Some(found)), - Err(exports_err) => { - if mode.is_types() && package_subpath == "." { - if let Ok(Some(path)) = - legacy_main_resolve::(&package_config, referrer_kind, mode) - { - return Ok(Some(path)); - } else { - return Ok(None); - } - } - return Err(exports_err); - } - } - } - if package_subpath == "." { - return legacy_main_resolve::(&package_config, referrer_kind, mode); - } - - Ok(Some(package_dir.join(package_subpath))) -} - -fn finalize_resolution( - resolved: ModuleSpecifier, - base: &ModuleSpecifier, -) -> Result { - let encoded_sep_re = lazy_regex::regex!(r"%2F|%2C"); - - if encoded_sep_re.is_match(resolved.path()) { - return Err(errors::err_invalid_module_specifier( - resolved.path(), - "must not include encoded \"/\" or \"\\\\\" characters", - Some(to_file_path_string(base)), - )); - } - - let path = to_file_path(&resolved); - - // TODO(bartlomieju): currently not supported - // if (getOptionValue('--experimental-specifier-resolution') === 'node') { - // ... - // } - - let p_str = path.to_str().unwrap(); - let p = if p_str.ends_with('/') { - p_str[p_str.len() - 1..].to_string() - } else { - p_str.to_string() - }; - - let (is_dir, is_file) = if let Ok(stats) = std::fs::metadata(p) { - (stats.is_dir(), stats.is_file()) - } else { - (false, false) - }; - if is_dir { - return Err(errors::err_unsupported_dir_import( - resolved.as_str(), - base.as_str(), - )); - } else if !is_file { - return Err(errors::err_module_not_found( - resolved.as_str(), - base.as_str(), - "module", - )); - } - - Ok(resolved) -} - -fn to_file_path(url: &ModuleSpecifier) -> PathBuf { - url - .to_file_path() - .unwrap_or_else(|_| panic!("Provided URL was not file:// URL: {url}")) -} - -fn to_file_path_string(url: &ModuleSpecifier) -> String { - to_file_path(url).display().to_string() -} - -fn should_be_treated_as_relative_or_absolute_path(specifier: &str) -> bool { - if specifier.is_empty() { - return false; - } - - if specifier.starts_with('/') { - return true; - } - - is_relative_specifier(specifier) -} - -// TODO(ry) We very likely have this utility function elsewhere in Deno. -fn is_relative_specifier(specifier: &str) -> bool { - let specifier_len = specifier.len(); - let specifier_chars: Vec<_> = specifier.chars().collect(); - - if !specifier_chars.is_empty() && specifier_chars[0] == '.' { - if specifier_len == 1 || specifier_chars[1] == '/' { - return true; - } - if specifier_chars[1] == '.' - && (specifier_len == 2 || specifier_chars[2] == '/') - { - return true; - } - } - false -} - -#[cfg(test)] -mod tests { - use deno_core::serde_json::json; - - use super::*; - - #[test] - fn test_resolve_bin_entry_value() { - // should resolve the specified value - let value = json!({ - "bin1": "./value1", - "bin2": "./value2", - "test": "./value3", - }); - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("test@1.1.1").unwrap(), - Some("bin1"), - &value - ) - .unwrap(), - "./value1" - ); - - // should resolve the value with the same name when not specified - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("test@1.1.1").unwrap(), - None, - &value - ) - .unwrap(), - "./value3" - ); - - // should not resolve when specified value does not exist - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("test@1.1.1").unwrap(), - Some("other"), - &value - ) - .err() - .unwrap() - .to_string(), - concat!( - "package 'test@1.1.1' did not have a bin entry for 'other' in its package.json\n", - "\n", - "Possibilities:\n", - " * npm:test@1.1.1/bin1\n", - " * npm:test@1.1.1/bin2\n", - " * npm:test@1.1.1/test" - ) - ); - - // should not resolve when default value can't be determined - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("asdf@1.2.3").unwrap(), - None, - &value - ) - .err() - .unwrap() - .to_string(), - concat!( - "package 'asdf@1.2.3' did not have a bin entry for 'asdf' in its package.json\n", - "\n", - "Possibilities:\n", - " * npm:asdf@1.2.3/bin1\n", - " * npm:asdf@1.2.3/bin2\n", - " * npm:asdf@1.2.3/test" - ) - ); - - // should resolve since all the values are the same - let value = json!({ - "bin1": "./value", - "bin2": "./value", - }); - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("test@1.2.3").unwrap(), - None, - &value - ) - .unwrap(), - "./value" - ); - - // should not resolve when specified and is a string - let value = json!("./value"); - assert_eq!( - resolve_bin_entry_value( - &NpmPackageNv::from_str("test@1.2.3").unwrap(), - Some("path"), - &value - ) - .err() - .unwrap() - .to_string(), - "package 'test@1.2.3' did not have a bin entry for 'path' in its package.json" - ); - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 8f6ac77bc..488f8eae6 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -13,6 +13,5 @@ pub use installer::PackageJsonDepsInstaller; pub use registry::CliNpmRegistryApi; pub use resolution::NpmResolution; pub use resolvers::create_npm_fs_resolver; -pub use resolvers::CliRequireNpmResolver; -pub use resolvers::NpmPackageResolver; +pub use resolvers::CliNpmResolver; pub use resolvers::NpmProcessState; diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs index 26fc356ff..1b191b245 100644 --- a/cli/npm/resolution.rs +++ b/cli/npm/resolution.rs @@ -154,7 +154,7 @@ impl NpmResolution { Ok(()) } - pub fn pkg_req_ref_to_nv_ref( + pub fn resolve_nv_ref_from_pkg_req_ref( &self, req_ref: &NpmPackageReqReference, ) -> Result { diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs index f693d3d23..8b871beaf 100644 --- a/cli/npm/resolvers/mod.rs +++ b/cli/npm/resolvers/mod.rs @@ -20,10 +20,12 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; +use deno_runtime::deno_node::NpmResolver; use deno_runtime::deno_node::PathClean; -use deno_runtime::deno_node::RequireNpmResolver; use deno_semver::npm::NpmPackageNv; +use deno_semver::npm::NpmPackageNvReference; use deno_semver::npm::NpmPackageReq; +use deno_semver::npm::NpmPackageReqReference; use global::GlobalNpmPackageResolver; use serde::Deserialize; use serde::Serialize; @@ -45,13 +47,13 @@ pub struct NpmProcessState { } /// Brings together the npm resolution with the file system. -pub struct NpmPackageResolver { +pub struct CliNpmResolver { fs_resolver: Arc, resolution: Arc, maybe_lockfile: Option>>, } -impl std::fmt::Debug for NpmPackageResolver { +impl std::fmt::Debug for CliNpmResolver { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("NpmPackageResolver") .field("fs_resolver", &"") @@ -61,7 +63,7 @@ impl std::fmt::Debug for NpmPackageResolver { } } -impl NpmPackageResolver { +impl CliNpmResolver { pub fn new( resolution: Arc, fs_resolver: Arc, @@ -85,15 +87,6 @@ impl NpmPackageResolver { self.resolution.resolve_pkg_id_from_pkg_req(req) } - /// Resolves an npm package folder path from a Deno module. - pub fn resolve_package_folder_from_deno_module( - &self, - pkg_nv: &NpmPackageNv, - ) -> Result { - let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(pkg_nv)?; - self.resolve_pkg_folder_from_deno_module_at_pkg_id(&pkg_id) - } - fn resolve_pkg_folder_from_deno_module_at_pkg_id( &self, pkg_id: &NpmPackageId, @@ -108,20 +101,6 @@ impl NpmPackageResolver { Ok(path) } - /// Resolves an npm package folder path from an npm package referrer. - pub fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - mode: NodeResolutionMode, - ) -> Result { - let path = self - .fs_resolver - .resolve_package_folder_from_package(name, referrer, mode)?; - log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); - Ok(path) - } - /// Resolve the root folder of the package the provided specifier is in. /// /// This will error when the provided specifier is not in an npm package. @@ -228,26 +207,20 @@ impl NpmPackageResolver { self.fs_resolver.cache_packages().await?; Ok(()) } - - pub fn as_require_npm_resolver(self: &Arc) -> CliRequireNpmResolver { - CliRequireNpmResolver(self.clone()) - } } -#[derive(Debug)] -pub struct CliRequireNpmResolver(Arc); - -impl RequireNpmResolver for CliRequireNpmResolver { +impl NpmResolver for CliNpmResolver { fn resolve_package_folder_from_package( &self, - specifier: &str, - referrer: &std::path::Path, + name: &str, + referrer: &ModuleSpecifier, mode: NodeResolutionMode, ) -> Result { - let referrer = path_to_specifier(referrer)?; - self - .0 - .resolve_package_folder_from_package(specifier, &referrer, mode) + let path = self + .fs_resolver + .resolve_package_folder_from_package(name, referrer, mode)?; + log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); + Ok(path) } fn resolve_package_folder_from_path( @@ -255,18 +228,34 @@ impl RequireNpmResolver for CliRequireNpmResolver { path: &Path, ) -> Result { let specifier = path_to_specifier(path)?; - self.0.resolve_package_folder_from_specifier(&specifier) + self.resolve_package_folder_from_specifier(&specifier) + } + + fn resolve_package_folder_from_deno_module( + &self, + pkg_nv: &NpmPackageNv, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(pkg_nv)?; + self.resolve_pkg_folder_from_deno_module_at_pkg_id(&pkg_id) + } + + fn resolve_pkg_id_from_pkg_req( + &self, + req: &NpmPackageReq, + ) -> Result { + self.resolution.resolve_pkg_id_from_pkg_req(req) + } + + fn resolve_nv_ref_from_pkg_req_ref( + &self, + req_ref: &NpmPackageReqReference, + ) -> Result { + self.resolution.resolve_nv_ref_from_pkg_req_ref(req_ref) } - fn in_npm_package(&self, path: &Path) -> bool { - let specifier = - match ModuleSpecifier::from_file_path(path.to_path_buf().clean()) { - Ok(p) => p, - Err(_) => return false, - }; + fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { self - .0 - .resolve_package_folder_from_specifier(&specifier) + .resolve_package_folder_from_specifier(specifier) .is_ok() } @@ -275,7 +264,7 @@ impl RequireNpmResolver for CliRequireNpmResolver { permissions: &mut dyn NodePermissions, path: &Path, ) -> Result<(), AnyError> { - self.0.fs_resolver.ensure_read_permission(permissions, path) + self.fs_resolver.ensure_read_permission(permissions, path) } } diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 9adc944ce..d39f19270 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use crate::npm::NpmPackageResolver; +use crate::npm::CliNpmResolver; use deno_core::error::AnyError; use deno_core::op; use deno_core::Extension; @@ -11,14 +11,14 @@ use deno_core::OpState; pub mod bench; pub mod testing; -pub fn cli_exts(npm_resolver: Arc) -> Vec { +pub fn cli_exts(npm_resolver: Arc) -> Vec { vec![deno_cli::init_ops(npm_resolver)] } deno_core::extension!(deno_cli, ops = [op_npm_process_state], options = { - npm_resolver: Arc, + npm_resolver: Arc, }, state = |state, options| { state.put(options.npm_resolver); @@ -30,6 +30,6 @@ deno_core::extension!(deno_cli, #[op] fn op_npm_process_state(state: &mut OpState) -> Result { - let npm_resolver = state.borrow_mut::>(); + let npm_resolver = state.borrow_mut::>(); Ok(npm_resolver.get_npm_process_state()) } diff --git a/cli/proc_state.rs b/cli/proc_state.rs index bfe45bc86..6b7e9b1f2 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -18,12 +18,12 @@ use crate::graph_util::ModuleGraphContainer; use crate::http_util::HttpClient; use crate::module_loader::ModuleLoadPreparer; use crate::node::CliCjsEsmCodeAnalyzer; +use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::npm::create_npm_fs_resolver; use crate::npm::CliNpmRegistryApi; -use crate::npm::CliRequireNpmResolver; +use crate::npm::CliNpmResolver; use crate::npm::NpmCache; -use crate::npm::NpmPackageResolver; use crate::npm::NpmResolution; use crate::npm::PackageJsonDepsInstaller; use crate::resolver::CliGraphResolver; @@ -39,6 +39,7 @@ use deno_core::SharedArrayBufferStore; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_node::analyze::NodeCodeTranslator; +use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_web::BlobStore; use deno_runtime::inspector_server::InspectorServer; @@ -77,12 +78,11 @@ pub struct Inner { maybe_file_watcher_reporter: Option, pub module_graph_builder: Arc, pub module_load_preparer: Arc, - pub node_code_translator: - Arc>, + pub node_code_translator: Arc, pub node_resolver: Arc, pub npm_api: Arc, pub npm_cache: Arc, - pub npm_resolver: Arc, + pub npm_resolver: Arc, pub npm_resolution: Arc, pub package_json_deps_installer: Arc, pub cjs_resolutions: Arc, @@ -252,7 +252,7 @@ impl ProcState { npm_resolution.clone(), cli_options.node_modules_dir_path(), ); - let npm_resolver = Arc::new(NpmPackageResolver::new( + let npm_resolver = Arc::new(CliNpmResolver::new( npm_resolution.clone(), npm_fs_resolver, lockfile.as_ref().cloned(), @@ -310,12 +310,9 @@ impl ProcState { let cjs_esm_analyzer = CliCjsEsmCodeAnalyzer::new(node_analysis_cache); let node_code_translator = Arc::new(NodeCodeTranslator::new( cjs_esm_analyzer, - npm_resolver.as_require_npm_resolver(), - )); - let node_resolver = Arc::new(CliNodeResolver::new( - npm_resolution.clone(), npm_resolver.clone(), )); + let node_resolver = Arc::new(NodeResolver::new(npm_resolver.clone())); let type_checker = Arc::new(TypeChecker::new( dir.clone(), caches.clone(), diff --git a/cli/tools/check.rs b/cli/tools/check.rs index c7f404223..36bc25d6a 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -22,7 +22,7 @@ use crate::cache::DenoDir; use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; use crate::node::CliNodeResolver; -use crate::npm::NpmPackageResolver; +use crate::npm::CliNpmResolver; use crate::tsc; use crate::version; @@ -43,7 +43,7 @@ pub struct TypeChecker { caches: Arc, cli_options: Arc, node_resolver: Arc, - npm_resolver: Arc, + npm_resolver: Arc, } impl TypeChecker { @@ -52,7 +52,7 @@ impl TypeChecker { caches: Arc, cli_options: Arc, node_resolver: Arc, - npm_resolver: Arc, + npm_resolver: Arc, ) -> Self { Self { deno_dir, diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 69faa10fb..a59f8a4c8 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -28,7 +28,7 @@ use crate::args::Flags; use crate::args::InfoFlags; use crate::display; use crate::graph_util::graph_lock_or_exit; -use crate::npm::NpmPackageResolver; +use crate::npm::CliNpmResolver; use crate::proc_state::ProcState; use crate::util::checksum; @@ -141,7 +141,7 @@ fn print_cache_info( fn add_npm_packages_to_json( json: &mut serde_json::Value, - npm_resolver: &NpmPackageResolver, + npm_resolver: &CliNpmResolver, ) { // ideally deno_graph could handle this, but for now we just modify the json here let snapshot = npm_resolver.snapshot(); @@ -318,7 +318,7 @@ struct NpmInfo { impl NpmInfo { pub fn build<'a>( graph: &'a ModuleGraph, - npm_resolver: &'a NpmPackageResolver, + npm_resolver: &'a CliNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) -> Self { let mut info = NpmInfo::default(); @@ -344,7 +344,7 @@ impl NpmInfo { fn fill_package_info<'a>( &mut self, package: &NpmResolutionPackage, - npm_resolver: &'a NpmPackageResolver, + npm_resolver: &'a CliNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) { self @@ -380,7 +380,7 @@ struct GraphDisplayContext<'a> { impl<'a> GraphDisplayContext<'a> { pub fn write( graph: &'a ModuleGraph, - npm_resolver: &'a NpmPackageResolver, + npm_resolver: &'a CliNpmResolver, writer: &mut TWrite, ) -> fmt::Result { let npm_snapshot = npm_resolver.snapshot(); diff --git a/cli/tools/task.rs b/cli/tools/task.rs index c64e2a77c..898cdd8d9 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -5,7 +5,7 @@ use crate::args::Flags; use crate::args::TaskFlags; use crate::colors; use crate::node::CliNodeResolver; -use crate::npm::NpmPackageResolver; +use crate::npm::CliNpmResolver; use crate::proc_state::ProcState; use crate::util::fs::canonicalize_path; use deno_core::anyhow::bail; @@ -13,6 +13,7 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::future::LocalBoxFuture; +use deno_runtime::deno_node::RealFs; use deno_semver::npm::NpmPackageNv; use deno_task_shell::ExecuteResult; use deno_task_shell::ShellCommand; @@ -234,13 +235,14 @@ impl ShellCommand for NpmPackageBinCommand { } fn resolve_npm_commands( - npm_resolver: &NpmPackageResolver, + npm_resolver: &CliNpmResolver, node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { let mut result = HashMap::new(); let snapshot = npm_resolver.snapshot(); for id in snapshot.top_level_packages() { - let bin_commands = node_resolver.resolve_binary_commands(&id.nv)?; + let bin_commands = + node_resolver.resolve_binary_commands::(&id.nv)?; for bin_command in bin_commands { result.insert( bin_command.to_string(), diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 57a4a1be8..0d956b661 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -5,7 +5,6 @@ use crate::args::TypeCheckMode; use crate::cache::FastInsecureHasher; use crate::node; use crate::node::CliNodeResolver; -use crate::node::NodeResolution; use crate::util::checksum; use crate::util::path::mapped_specifier_for_tsc; @@ -33,7 +32,10 @@ use deno_core::Snapshot; use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; +use deno_runtime::deno_node; +use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolutionMode; +use deno_runtime::deno_node::RealFs; use deno_runtime::permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReqReference; use lsp_types::Url; @@ -537,7 +539,7 @@ fn op_resolve( }; for specifier in args.specifiers { if let Some(module_name) = specifier.strip_prefix("node:") { - if crate::node::resolve_builtin_node_module(module_name).is_ok() { + if deno_node::resolve_builtin_node_module(module_name).is_ok() { // return itself for node: specifiers because during type checking // we resolve to the ambient modules in the @types/node package // rather than deno_std/node @@ -635,7 +637,7 @@ fn resolve_graph_specifier_types( } Some(Module::Npm(module)) => { if let Some(node_resolver) = &state.maybe_node_resolver { - let maybe_resolution = node_resolver.resolve_npm_reference( + let maybe_resolution = node_resolver.resolve_npm_reference::( &module.nv_reference, NodeResolutionMode::Types, &mut PermissionsContainer::allow_all(), @@ -653,7 +655,9 @@ fn resolve_graph_specifier_types( let specifier = node::resolve_specifier_into_node_modules(&module.specifier); NodeResolution::into_specifier_and_media_type( - node_resolver.url_to_node_resolution(specifier).ok(), + node_resolver + .url_to_node_resolution::(specifier) + .ok(), ) })) } @@ -674,7 +678,7 @@ fn resolve_non_graph_specifier_types( // we're in an npm package, so use node resolution Ok(Some(NodeResolution::into_specifier_and_media_type( node_resolver - .resolve( + .resolve::( specifier, referrer, NodeResolutionMode::Types, @@ -688,7 +692,7 @@ fn resolve_non_graph_specifier_types( // we don't need this special code here. // This could occur when resolving npm:@types/node when it is // injected and not part of the graph - let maybe_resolution = node_resolver.resolve_npm_req_reference( + let maybe_resolution = node_resolver.resolve_npm_req_reference::( &npm_ref, NodeResolutionMode::Types, &mut PermissionsContainer::allow_all(), diff --git a/cli/worker.rs b/cli/worker.rs index 7ee8fc802..c73e4edbe 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -13,6 +13,8 @@ use deno_core::Extension; use deno_core::ModuleId; use deno_runtime::colors; use deno_runtime::deno_node; +use deno_runtime::deno_node::NodeResolution; +use deno_runtime::deno_node::RealFs; use deno_runtime::fmt_errors::format_js_error; use deno_runtime::ops::worker_host::CreateWebWorkerCb; use deno_runtime::ops::worker_host::WorkerEventCb; @@ -27,7 +29,6 @@ use deno_semver::npm::NpmPackageReqReference; use crate::args::DenoSubcommand; use crate::errors; use crate::module_loader::CliModuleLoader; -use crate::node; use crate::ops; use crate::proc_state::ProcState; use crate::tools; @@ -258,16 +259,16 @@ pub async fn create_custom_worker( ps.npm_resolver .add_package_reqs(vec![package_ref.req.clone()]) .await?; - let node_resolution = - ps.node_resolver.resolve_binary_export(&package_ref)?; - let is_main_cjs = - matches!(node_resolution, node::NodeResolution::CommonJs(_)); + let node_resolution = ps + .node_resolver + .resolve_binary_export::(&package_ref)?; + let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_)); (node_resolution.into_url(), is_main_cjs) } else if ps.options.is_npm_main() { - let node_resolution = - ps.node_resolver.url_to_node_resolution(main_module)?; - let is_main_cjs = - matches!(node_resolution, node::NodeResolution::CommonJs(_)); + let node_resolution = ps + .node_resolver + .url_to_node_resolution::(main_module)?; + let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_)); (node_resolution.into_url(), is_main_cjs) } else { (main_module, false) @@ -344,7 +345,7 @@ pub async fn create_custom_worker( should_break_on_first_statement: ps.options.inspect_brk().is_some(), should_wait_for_inspector_session: ps.options.inspect_wait().is_some(), module_loader, - npm_resolver: Some(Rc::new(ps.npm_resolver.as_require_npm_resolver())), + npm_resolver: Some(Rc::new(ps.npm_resolver.clone())), get_error_class_fn: Some(&errors::get_error_class_name), cache_storage_dir, origin_storage_dir, @@ -467,7 +468,7 @@ fn create_web_worker_callback( format_js_error_fn: Some(Arc::new(format_js_error)), source_map_getter: Some(Box::new(module_loader.clone())), module_loader, - npm_resolver: Some(Rc::new(ps.npm_resolver.as_require_npm_resolver())), + npm_resolver: Some(Rc::new(ps.npm_resolver.clone())), worker_type: args.worker_type, maybe_inspector_server, get_error_class_fn: Some(&errors::get_error_class_name), -- cgit v1.2.3