mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
feat: support node built-in module imports (#17264)
Co-authored-by: David Sherret <dsherret@gmail.com>
This commit is contained in:
parent
cadeaae045
commit
fc2e00152b
32 changed files with 925 additions and 445 deletions
|
@ -17,6 +17,7 @@ mod ts {
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::op;
|
||||
use deno_core::OpState;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
@ -164,10 +165,16 @@ mod ts {
|
|||
#[op]
|
||||
fn op_build_info(state: &mut OpState) -> Value {
|
||||
let build_specifier = "asset:///bootstrap.ts";
|
||||
|
||||
let node_built_in_module_names = SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.iter()
|
||||
.map(|s| s.name)
|
||||
.collect::<Vec<&str>>();
|
||||
let build_libs = state.borrow::<Vec<&str>>();
|
||||
json!({
|
||||
"buildSpecifier": build_specifier,
|
||||
"libs": build_libs,
|
||||
"nodeBuiltInModuleNames": node_built_in_module_names,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
23
cli/cache/mod.rs
vendored
23
cli/cache/mod.rs
vendored
|
@ -65,7 +65,7 @@ impl FetchCacher {
|
|||
|
||||
impl Loader for FetchCacher {
|
||||
fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> {
|
||||
if specifier.scheme() == "npm" {
|
||||
if matches!(specifier.scheme(), "npm" | "node") {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,26 @@ impl Loader for FetchCacher {
|
|||
));
|
||||
}
|
||||
|
||||
let specifier = specifier.clone();
|
||||
let specifier =
|
||||
if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
|
||||
if module_name == "module" {
|
||||
// the source code for "node:module" is built-in rather than
|
||||
// being from deno_std like the other modules
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
deno_graph::source::LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
|
||||
match crate::node::resolve_builtin_node_module(module_name) {
|
||||
Ok(specifier) => specifier,
|
||||
Err(err) => return Box::pin(futures::future::ready(Err(err))),
|
||||
}
|
||||
} else {
|
||||
specifier.clone()
|
||||
};
|
||||
|
||||
let permissions = if is_dynamic {
|
||||
self.dynamic_permissions.clone()
|
||||
} else {
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::cache;
|
|||
use crate::cache::TypeCheckCache;
|
||||
use crate::colors;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::npm::resolve_npm_package_reqs;
|
||||
use crate::npm::resolve_graph_npm_info;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::npm::NpmPackageReq;
|
||||
use crate::proc_state::ProcState;
|
||||
|
@ -25,6 +25,7 @@ use deno_graph::GraphImport;
|
|||
use deno_graph::MediaType;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use deno_graph::ModuleKind;
|
||||
use deno_graph::Range;
|
||||
use deno_graph::Resolved;
|
||||
use deno_runtime::permissions::PermissionsContainer;
|
||||
|
@ -54,7 +55,10 @@ pub enum ModuleEntry {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct GraphData {
|
||||
modules: HashMap<ModuleSpecifier, ModuleEntry>,
|
||||
/// Specifiers that are built-in or external.
|
||||
external_specifiers: HashSet<ModuleSpecifier>,
|
||||
npm_packages: Vec<NpmPackageReq>,
|
||||
has_node_builtin_specifier: bool,
|
||||
/// Map of first known referrer locations for each module. Used to enhance
|
||||
/// error messages.
|
||||
referrer_map: HashMap<ModuleSpecifier, Box<Range>>,
|
||||
|
@ -83,13 +87,12 @@ impl GraphData {
|
|||
let mut has_npm_specifier_in_graph = false;
|
||||
|
||||
for (specifier, result) in graph.specifiers() {
|
||||
if NpmPackageReference::from_specifier(specifier).is_ok() {
|
||||
has_npm_specifier_in_graph = true;
|
||||
if self.modules.contains_key(specifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.modules.contains_key(specifier) {
|
||||
continue;
|
||||
if !self.has_node_builtin_specifier && specifier.scheme() == "node" {
|
||||
self.has_node_builtin_specifier = true;
|
||||
}
|
||||
|
||||
if let Some(found) = graph.redirects.get(specifier) {
|
||||
|
@ -97,8 +100,19 @@ impl GraphData {
|
|||
self.modules.insert(specifier.clone(), module_entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok((_, _, media_type)) => {
|
||||
Ok((_, module_kind, media_type)) => {
|
||||
if module_kind == ModuleKind::External {
|
||||
if !has_npm_specifier_in_graph
|
||||
&& NpmPackageReference::from_specifier(specifier).is_ok()
|
||||
{
|
||||
has_npm_specifier_in_graph = true;
|
||||
}
|
||||
self.external_specifiers.insert(specifier.clone());
|
||||
continue; // ignore npm and node specifiers
|
||||
}
|
||||
|
||||
let module = graph.get(specifier).unwrap();
|
||||
let code = match &module.maybe_source {
|
||||
Some(source) => source.clone(),
|
||||
|
@ -147,7 +161,9 @@ impl GraphData {
|
|||
}
|
||||
|
||||
if has_npm_specifier_in_graph {
|
||||
self.npm_packages.extend(resolve_npm_package_reqs(graph));
|
||||
self
|
||||
.npm_packages
|
||||
.extend(resolve_graph_npm_info(graph).package_reqs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,6 +173,11 @@ impl GraphData {
|
|||
self.modules.iter()
|
||||
}
|
||||
|
||||
/// Gets if the graph had a "node:" specifier.
|
||||
pub fn has_node_builtin_specifier(&self) -> bool {
|
||||
self.has_node_builtin_specifier
|
||||
}
|
||||
|
||||
/// Gets the npm package requirements from all the encountered graphs
|
||||
/// in the order that they should be resolved.
|
||||
pub fn npm_package_reqs(&self) -> &Vec<NpmPackageReq> {
|
||||
|
@ -195,13 +216,14 @@ impl GraphData {
|
|||
}
|
||||
}
|
||||
while let Some(specifier) = visiting.pop_front() {
|
||||
if NpmPackageReference::from_specifier(specifier).is_ok() {
|
||||
continue; // skip analyzing npm specifiers
|
||||
}
|
||||
|
||||
let (specifier, entry) = match self.modules.get_key_value(specifier) {
|
||||
Some(pair) => pair,
|
||||
None => return None,
|
||||
None => {
|
||||
if self.external_specifiers.contains(specifier) {
|
||||
continue;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
};
|
||||
result.insert(specifier, entry);
|
||||
match entry {
|
||||
|
@ -281,6 +303,8 @@ impl GraphData {
|
|||
}
|
||||
Some(Self {
|
||||
modules,
|
||||
external_specifiers: self.external_specifiers.clone(),
|
||||
has_node_builtin_specifier: self.has_node_builtin_specifier,
|
||||
npm_packages: self.npm_packages.clone(),
|
||||
referrer_map,
|
||||
graph_imports: self.graph_imports.to_vec(),
|
||||
|
@ -547,6 +571,14 @@ pub async fn create_graph_and_maybe_check(
|
|||
}
|
||||
|
||||
if ps.options.type_check_mode() != TypeCheckMode::None {
|
||||
// node built-in specifiers use the @types/node package to determine
|
||||
// types, so inject that now after the lockfile has been written
|
||||
if graph_data.has_node_builtin_specifier() {
|
||||
ps.npm_resolver
|
||||
.inject_synthetic_types_node_package()
|
||||
.await?;
|
||||
}
|
||||
|
||||
let ts_config_result =
|
||||
ps.options.resolve_ts_config_for_emit(TsConfigType::Check {
|
||||
lib: ps.options.ts_type_lib_window(),
|
||||
|
|
|
@ -68,7 +68,7 @@ impl CacheMetadata {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<Arc<HashMap<MetadataKey, String>>> {
|
||||
if specifier.scheme() == "file" || specifier.scheme() == "npm" {
|
||||
if matches!(specifier.scheme(), "file" | "npm" | "node") {
|
||||
return None;
|
||||
}
|
||||
let version = self
|
||||
|
|
|
@ -13,6 +13,7 @@ use super::tsc;
|
|||
use super::tsc::TsServer;
|
||||
|
||||
use crate::args::LintOptions;
|
||||
use crate::node;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::tools::lint::get_configured_rules;
|
||||
|
||||
|
@ -614,6 +615,8 @@ pub enum DenoDiagnostic {
|
|||
},
|
||||
/// An error occurred when resolving the specifier string.
|
||||
ResolutionError(deno_graph::ResolutionError),
|
||||
/// Invalid `node:` specifier.
|
||||
InvalidNodeSpecifier(ModuleSpecifier),
|
||||
}
|
||||
|
||||
impl DenoDiagnostic {
|
||||
|
@ -641,6 +644,7 @@ impl DenoDiagnostic {
|
|||
},
|
||||
ResolutionError::ResolverError { .. } => "resolver-error",
|
||||
},
|
||||
Self::InvalidNodeSpecifier(_) => "resolver-error",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,6 +795,7 @@ impl DenoDiagnostic {
|
|||
Self::NoLocal(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier), None),
|
||||
Self::Redirect { from, to} => (lsp::DiagnosticSeverity::INFORMATION, format!("The import of \"{}\" was redirected to \"{}\".", from, to), Some(json!({ "specifier": from, "redirect": to }))),
|
||||
Self::ResolutionError(err) => (lsp::DiagnosticSeverity::ERROR, err.to_string(), None),
|
||||
Self::InvalidNodeSpecifier(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unknown Node built-in module: {}", specifier.path()), None),
|
||||
};
|
||||
lsp::Diagnostic {
|
||||
range: *range,
|
||||
|
@ -872,6 +877,30 @@ fn diagnose_resolved(
|
|||
);
|
||||
}
|
||||
}
|
||||
} else if let Some(module_name) = specifier.as_str().strip_prefix("node:")
|
||||
{
|
||||
if node::resolve_builtin_node_module(module_name).is_err() {
|
||||
diagnostics.push(
|
||||
DenoDiagnostic::InvalidNodeSpecifier(specifier.clone())
|
||||
.to_lsp_diagnostic(&range),
|
||||
);
|
||||
} else if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
|
||||
// check that a @types/node package exists in the resolver
|
||||
let types_node_ref =
|
||||
NpmPackageReference::from_str("npm:@types/node").unwrap();
|
||||
if npm_resolver
|
||||
.resolve_package_folder_from_deno_module(&types_node_ref.req)
|
||||
.is_err()
|
||||
{
|
||||
diagnostics.push(
|
||||
DenoDiagnostic::NoCacheNpm(
|
||||
types_node_ref,
|
||||
ModuleSpecifier::parse("npm:@types/node").unwrap(),
|
||||
)
|
||||
.to_lsp_diagnostic(&range),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// When the document is not available, it means that it cannot be found
|
||||
// in the cache or locally on the disk, so we want to issue a diagnostic
|
||||
|
|
|
@ -770,6 +770,9 @@ pub struct Documents {
|
|||
maybe_resolver: Option<CliResolver>,
|
||||
/// The npm package requirements.
|
||||
npm_reqs: Arc<HashSet<NpmPackageReq>>,
|
||||
/// Gets if any document had a node: specifier such that a @types/node package
|
||||
/// should be injected.
|
||||
has_injected_types_node_package: bool,
|
||||
/// Resolves a specifier to its final redirected to specifier.
|
||||
specifier_resolver: Arc<SpecifierResolver>,
|
||||
}
|
||||
|
@ -785,6 +788,7 @@ impl Documents {
|
|||
imports: Default::default(),
|
||||
maybe_resolver: None,
|
||||
npm_reqs: Default::default(),
|
||||
has_injected_types_node_package: false,
|
||||
specifier_resolver: Arc::new(SpecifierResolver::new(location)),
|
||||
}
|
||||
}
|
||||
|
@ -925,6 +929,12 @@ impl Documents {
|
|||
(*self.npm_reqs).clone()
|
||||
}
|
||||
|
||||
/// Returns if a @types/node package was injected into the npm
|
||||
/// resolver based on the state of the documents.
|
||||
pub fn has_injected_types_node_package(&self) -> bool {
|
||||
self.has_injected_types_node_package
|
||||
}
|
||||
|
||||
/// Return a document for the specifier.
|
||||
pub fn get(&self, original_specifier: &ModuleSpecifier) -> Option<Document> {
|
||||
let specifier = self.specifier_resolver.resolve(original_specifier)?;
|
||||
|
@ -985,11 +995,15 @@ impl Documents {
|
|||
/// tsc when type checking.
|
||||
pub fn resolve(
|
||||
&self,
|
||||
specifiers: &[String],
|
||||
referrer: &ModuleSpecifier,
|
||||
specifiers: Vec<String>,
|
||||
referrer_doc: &AssetOrDocument,
|
||||
maybe_npm_resolver: Option<&NpmPackageResolver>,
|
||||
) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> {
|
||||
let dependencies = self.get(referrer)?.0.dependencies.clone();
|
||||
) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
|
||||
let referrer = referrer_doc.specifier();
|
||||
let dependencies = match referrer_doc {
|
||||
AssetOrDocument::Asset(_) => None,
|
||||
AssetOrDocument::Document(doc) => Some(doc.0.dependencies.clone()),
|
||||
};
|
||||
let mut results = Vec::new();
|
||||
for specifier in specifiers {
|
||||
if let Some(npm_resolver) = maybe_npm_resolver {
|
||||
|
@ -997,7 +1011,7 @@ impl Documents {
|
|||
// we're in an npm package, so use node resolution
|
||||
results.push(Some(NodeResolution::into_specifier_and_media_type(
|
||||
node::node_resolve(
|
||||
specifier,
|
||||
&specifier,
|
||||
referrer,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
|
@ -1009,15 +1023,28 @@ impl Documents {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
// handle npm:<package> urls
|
||||
if let Some(module_name) = specifier.strip_prefix("node:") {
|
||||
if crate::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
|
||||
results.push(Some((
|
||||
ModuleSpecifier::parse(&specifier).unwrap(),
|
||||
MediaType::Dts,
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if specifier.starts_with("asset:") {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(specifier) {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(&specifier) {
|
||||
let media_type = MediaType::from(&specifier);
|
||||
results.push(Some((specifier, media_type)));
|
||||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
} else if let Some(dep) = dependencies.deps.get(specifier) {
|
||||
} else if let Some(dep) =
|
||||
dependencies.as_ref().and_then(|d| d.deps.get(&specifier))
|
||||
{
|
||||
if let Resolved::Ok { specifier, .. } = &dep.maybe_type {
|
||||
results.push(self.resolve_dependency(specifier, maybe_npm_resolver));
|
||||
} else if let Resolved::Ok { specifier, .. } = &dep.maybe_code {
|
||||
|
@ -1026,12 +1053,12 @@ impl Documents {
|
|||
results.push(None);
|
||||
}
|
||||
} else if let Some(Resolved::Ok { specifier, .. }) =
|
||||
self.resolve_imports_dependency(specifier)
|
||||
self.resolve_imports_dependency(&specifier)
|
||||
{
|
||||
// clone here to avoid double borrow of self
|
||||
let specifier = specifier.clone();
|
||||
results.push(self.resolve_dependency(&specifier, maybe_npm_resolver));
|
||||
} else if let Ok(npm_ref) = NpmPackageReference::from_str(specifier) {
|
||||
} else if let Ok(npm_ref) = NpmPackageReference::from_str(&specifier) {
|
||||
results.push(maybe_npm_resolver.map(|npm_resolver| {
|
||||
NodeResolution::into_specifier_and_media_type(
|
||||
node_resolve_npm_reference(
|
||||
|
@ -1048,7 +1075,7 @@ impl Documents {
|
|||
results.push(None);
|
||||
}
|
||||
}
|
||||
Some(results)
|
||||
results
|
||||
}
|
||||
|
||||
/// Update the location of the on disk cache for the document store.
|
||||
|
@ -1125,6 +1152,7 @@ impl Documents {
|
|||
analyzed_specifiers: HashSet<ModuleSpecifier>,
|
||||
pending_specifiers: VecDeque<ModuleSpecifier>,
|
||||
npm_reqs: HashSet<NpmPackageReq>,
|
||||
has_node_builtin_specifier: bool,
|
||||
}
|
||||
|
||||
impl DocAnalyzer {
|
||||
|
@ -1148,7 +1176,11 @@ impl Documents {
|
|||
|
||||
fn analyze_doc(&mut self, specifier: &ModuleSpecifier, doc: &Document) {
|
||||
self.analyzed_specifiers.insert(specifier.clone());
|
||||
for dependency in doc.dependencies().values() {
|
||||
for (name, dependency) in doc.dependencies() {
|
||||
if !self.has_node_builtin_specifier && name.starts_with("node:") {
|
||||
self.has_node_builtin_specifier = true;
|
||||
}
|
||||
|
||||
if let Some(dep) = dependency.get_code() {
|
||||
self.add(dep, specifier);
|
||||
}
|
||||
|
@ -1185,8 +1217,19 @@ impl Documents {
|
|||
}
|
||||
}
|
||||
|
||||
let mut npm_reqs = doc_analyzer.npm_reqs;
|
||||
// Ensure a @types/node package exists when any module uses a node: specifier.
|
||||
// Unlike on the command line, here we just add @types/node to the npm package
|
||||
// requirements since this won't end up in the lockfile.
|
||||
self.has_injected_types_node_package = doc_analyzer
|
||||
.has_node_builtin_specifier
|
||||
&& !npm_reqs.iter().any(|r| r.name == "@types/node");
|
||||
if self.has_injected_types_node_package {
|
||||
npm_reqs.insert(NpmPackageReq::from_str("@types/node").unwrap());
|
||||
}
|
||||
|
||||
self.dependents_map = Arc::new(doc_analyzer.dependents_map);
|
||||
self.npm_reqs = Arc::new(doc_analyzer.npm_reqs);
|
||||
self.npm_reqs = Arc::new(npm_reqs);
|
||||
self.dirty = false;
|
||||
file_system_docs.dirty = false;
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ fn new_assets_map() -> Arc<Mutex<AssetsMap>> {
|
|||
let asset = AssetDocument::new(specifier.clone(), v);
|
||||
(specifier, asset)
|
||||
})
|
||||
.collect();
|
||||
.collect::<AssetsMap>();
|
||||
Arc::new(Mutex::new(assets))
|
||||
}
|
||||
|
||||
|
@ -2728,28 +2728,29 @@ fn op_resolve(
|
|||
let state = state.borrow_mut::<State>();
|
||||
let mark = state.performance.mark("op_resolve", Some(&args));
|
||||
let referrer = state.normalize_specifier(&args.base)?;
|
||||
|
||||
let result = if let Some(resolved) = state.state_snapshot.documents.resolve(
|
||||
&args.specifiers,
|
||||
&referrer,
|
||||
state.state_snapshot.maybe_npm_resolver.as_ref(),
|
||||
) {
|
||||
Ok(
|
||||
resolved
|
||||
.into_iter()
|
||||
.map(|o| {
|
||||
o.map(|(s, mt)| (s.to_string(), mt.as_ts_extension().to_string()))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
Err(custom_error(
|
||||
let result = match state.get_asset_or_document(&referrer) {
|
||||
Some(referrer_doc) => {
|
||||
let resolved = state.state_snapshot.documents.resolve(
|
||||
args.specifiers,
|
||||
&referrer_doc,
|
||||
state.state_snapshot.maybe_npm_resolver.as_ref(),
|
||||
);
|
||||
Ok(
|
||||
resolved
|
||||
.into_iter()
|
||||
.map(|o| {
|
||||
o.map(|(s, mt)| (s.to_string(), mt.as_ts_extension().to_string()))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
None => Err(custom_error(
|
||||
"NotFound",
|
||||
format!(
|
||||
"Error resolving. Referring specifier \"{}\" was not found.",
|
||||
args.base
|
||||
),
|
||||
))
|
||||
)),
|
||||
};
|
||||
|
||||
state.performance.measure(mark);
|
||||
|
@ -2764,15 +2765,20 @@ fn op_respond(state: &mut OpState, args: Response) -> bool {
|
|||
}
|
||||
|
||||
#[op]
|
||||
fn op_script_names(state: &mut OpState) -> Vec<ModuleSpecifier> {
|
||||
fn op_script_names(state: &mut OpState) -> Vec<String> {
|
||||
let state = state.borrow_mut::<State>();
|
||||
state
|
||||
.state_snapshot
|
||||
.documents
|
||||
.documents(true, true)
|
||||
.into_iter()
|
||||
.map(|d| d.specifier().clone())
|
||||
.collect()
|
||||
let documents = &state.state_snapshot.documents;
|
||||
let open_docs = documents.documents(true, true);
|
||||
|
||||
let mut result = Vec::with_capacity(open_docs.len() + 1);
|
||||
|
||||
if documents.has_injected_types_node_package() {
|
||||
// ensure this is first so it resolves the node types first
|
||||
result.push("asset:///node_types.d.ts".to_string());
|
||||
}
|
||||
|
||||
result.extend(open_docs.into_iter().map(|d| d.specifier().to_string()));
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
|
202
cli/node/mod.rs
202
cli/node/mod.rs
|
@ -25,6 +25,7 @@ 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::NodeModulePolyfill;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::deno_node::NodeResolutionMode;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
|
@ -32,6 +33,7 @@ use deno_runtime::deno_node::PathClean;
|
|||
use deno_runtime::deno_node::RequireNpmResolver;
|
||||
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
|
||||
use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
use deno_runtime::permissions::PermissionsContainer;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
@ -106,200 +108,6 @@ impl NodeResolution {
|
|||
}
|
||||
}
|
||||
|
||||
struct NodeModulePolyfill {
|
||||
/// Name of the module like "assert" or "timers/promises"
|
||||
name: &'static str,
|
||||
|
||||
/// Specifier relative to the root of `deno_std` repo, like "node/asser.ts"
|
||||
specifier: &'static str,
|
||||
}
|
||||
|
||||
static SUPPORTED_MODULES: &[NodeModulePolyfill] = &[
|
||||
NodeModulePolyfill {
|
||||
name: "assert",
|
||||
specifier: "node/assert.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "assert/strict",
|
||||
specifier: "node/assert/strict.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "async_hooks",
|
||||
specifier: "node/async_hooks.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "buffer",
|
||||
specifier: "node/buffer.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "child_process",
|
||||
specifier: "node/child_process.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "cluster",
|
||||
specifier: "node/cluster.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "console",
|
||||
specifier: "node/console.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "constants",
|
||||
specifier: "node/constants.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "crypto",
|
||||
specifier: "node/crypto.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dgram",
|
||||
specifier: "node/dgram.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dns",
|
||||
specifier: "node/dns.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dns/promises",
|
||||
specifier: "node/dns/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "domain",
|
||||
specifier: "node/domain.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "events",
|
||||
specifier: "node/events.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "fs",
|
||||
specifier: "node/fs.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "fs/promises",
|
||||
specifier: "node/fs/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "http",
|
||||
specifier: "node/http.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "https",
|
||||
specifier: "node/https.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "module",
|
||||
// NOTE(bartlomieju): `module` is special, because we don't want to use
|
||||
// `deno_std/node/module.ts`, but instead use a special shim that we
|
||||
// provide in `ext/node`.
|
||||
specifier: "[USE `deno_node::MODULE_ES_SHIM` to get this module]",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "net",
|
||||
specifier: "node/net.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "os",
|
||||
specifier: "node/os.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path",
|
||||
specifier: "node/path.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path/posix",
|
||||
specifier: "node/path/posix.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path/win32",
|
||||
specifier: "node/path/win32.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "perf_hooks",
|
||||
specifier: "node/perf_hooks.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "process",
|
||||
specifier: "node/process.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "querystring",
|
||||
specifier: "node/querystring.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "readline",
|
||||
specifier: "node/readline.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream",
|
||||
specifier: "node/stream.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/consumers",
|
||||
specifier: "node/stream/consumers.mjs",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/promises",
|
||||
specifier: "node/stream/promises.mjs",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/web",
|
||||
specifier: "node/stream/web.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "string_decoder",
|
||||
specifier: "node/string_decoder.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "sys",
|
||||
specifier: "node/sys.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "timers",
|
||||
specifier: "node/timers.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "timers/promises",
|
||||
specifier: "node/timers/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "tls",
|
||||
specifier: "node/tls.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "tty",
|
||||
specifier: "node/tty.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "url",
|
||||
specifier: "node/url.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "util",
|
||||
specifier: "node/util.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "util/types",
|
||||
specifier: "node/util/types.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "v8",
|
||||
specifier: "node/v8.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "vm",
|
||||
specifier: "node/vm.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "worker_threads",
|
||||
specifier: "node/worker_threads.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "zlib",
|
||||
specifier: "node/zlib.ts",
|
||||
},
|
||||
];
|
||||
|
||||
static NODE_COMPAT_URL: Lazy<Url> = Lazy::new(|| {
|
||||
if let Ok(url_str) = std::env::var("DENO_NODE_COMPAT_URL") {
|
||||
let url = Url::parse(&url_str).expect(
|
||||
|
@ -315,7 +123,9 @@ pub static MODULE_ALL_URL: Lazy<Url> =
|
|||
Lazy::new(|| NODE_COMPAT_URL.join("node/module_all.ts").unwrap());
|
||||
|
||||
fn find_builtin_node_module(specifier: &str) -> Option<&NodeModulePolyfill> {
|
||||
SUPPORTED_MODULES.iter().find(|m| m.name == specifier)
|
||||
SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.iter()
|
||||
.find(|m| m.name == specifier)
|
||||
}
|
||||
|
||||
fn is_builtin_node_module(specifier: &str) -> bool {
|
||||
|
@ -336,7 +146,7 @@ pub fn resolve_builtin_node_module(specifier: &str) -> Result<Url, AnyError> {
|
|||
}
|
||||
|
||||
Err(generic_error(format!(
|
||||
"Unknown built-in Node module: {}",
|
||||
"Unknown built-in \"node:\" module: {}",
|
||||
specifier
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ pub use cache::NpmCache;
|
|||
pub use registry::NpmPackageVersionDistInfo;
|
||||
pub use registry::NpmRegistryApi;
|
||||
pub use registry::RealNpmRegistryApi;
|
||||
pub use resolution::resolve_npm_package_reqs;
|
||||
pub use resolution::resolve_graph_npm_info;
|
||||
pub use resolution::NpmPackageId;
|
||||
pub use resolution::NpmPackageReference;
|
||||
pub use resolution::NpmPackageReq;
|
||||
|
|
|
@ -1081,7 +1081,7 @@ fn tag_to_version_info<'a>(
|
|||
// explicit version.
|
||||
if tag == "latest" && info.name == "@types/node" {
|
||||
return get_resolved_package_version_and_info(
|
||||
&NpmVersionReq::parse("18.0.0 - 18.8.2").unwrap(),
|
||||
&NpmVersionReq::parse("18.0.0 - 18.11.18").unwrap(),
|
||||
info,
|
||||
parent,
|
||||
);
|
||||
|
|
|
@ -28,7 +28,7 @@ mod specifier;
|
|||
|
||||
use graph::Graph;
|
||||
pub use snapshot::NpmResolutionSnapshot;
|
||||
pub use specifier::resolve_npm_package_reqs;
|
||||
pub use specifier::resolve_graph_npm_info;
|
||||
pub use specifier::NpmPackageReference;
|
||||
pub use specifier::NpmPackageReq;
|
||||
|
||||
|
|
|
@ -168,8 +168,15 @@ impl NpmVersionMatcher for NpmPackageReq {
|
|||
}
|
||||
}
|
||||
|
||||
/// Resolves the npm package requirements from the graph attempting. The order
|
||||
/// returned is the order they should be resolved in.
|
||||
pub struct GraphNpmInfo {
|
||||
/// The order of these package requirements is the order they
|
||||
/// should be resolved in.
|
||||
pub package_reqs: Vec<NpmPackageReq>,
|
||||
/// Gets if the graph had a built-in node specifier (ex. `node:fs`).
|
||||
pub has_node_builtin_specifier: bool,
|
||||
}
|
||||
|
||||
/// Resolves npm specific information from the graph.
|
||||
///
|
||||
/// This function will analyze the module graph for parent-most folder
|
||||
/// specifiers of all modules, then group npm specifiers together as found in
|
||||
|
@ -211,7 +218,7 @@ impl NpmVersionMatcher for NpmPackageReq {
|
|||
///
|
||||
/// Then it would resolve the npm specifiers in each of those groups according
|
||||
/// to that tree going by tree depth.
|
||||
pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
|
||||
pub fn resolve_graph_npm_info(graph: &ModuleGraph) -> GraphNpmInfo {
|
||||
fn collect_specifiers<'a>(
|
||||
graph: &'a ModuleGraph,
|
||||
module: &'a deno_graph::Module,
|
||||
|
@ -248,6 +255,7 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
|
|||
graph: &ModuleGraph,
|
||||
specifier_graph: &mut SpecifierTree,
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
has_node_builtin_specifier: &mut bool,
|
||||
) {
|
||||
if !seen.insert(module.specifier.clone()) {
|
||||
return; // already visited
|
||||
|
@ -267,12 +275,22 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
|
|||
.dependencies
|
||||
.insert(get_folder_path_specifier(specifier));
|
||||
}
|
||||
|
||||
if !*has_node_builtin_specifier && specifier.scheme() == "node" {
|
||||
*has_node_builtin_specifier = true;
|
||||
}
|
||||
}
|
||||
|
||||
// now visit all the dependencies
|
||||
for specifier in &specifiers {
|
||||
if let Some(module) = graph.get(specifier) {
|
||||
analyze_module(module, graph, specifier_graph, seen);
|
||||
analyze_module(
|
||||
module,
|
||||
graph,
|
||||
specifier_graph,
|
||||
seen,
|
||||
has_node_builtin_specifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,9 +302,16 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
|
|||
.collect::<Vec<_>>();
|
||||
let mut seen = HashSet::new();
|
||||
let mut specifier_graph = SpecifierTree::default();
|
||||
let mut has_node_builtin_specifier = false;
|
||||
for root in &root_specifiers {
|
||||
if let Some(module) = graph.get(root) {
|
||||
analyze_module(module, graph, &mut specifier_graph, &mut seen);
|
||||
analyze_module(
|
||||
module,
|
||||
graph,
|
||||
&mut specifier_graph,
|
||||
&mut seen,
|
||||
&mut has_node_builtin_specifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +349,10 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
|
|||
}
|
||||
}
|
||||
|
||||
result
|
||||
GraphNpmInfo {
|
||||
has_node_builtin_specifier,
|
||||
package_reqs: result,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_folder_path_specifier(specifier: &ModuleSpecifier) -> ModuleSpecifier {
|
||||
|
@ -979,7 +1007,8 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.await;
|
||||
let reqs = resolve_npm_package_reqs(&graph)
|
||||
let reqs = resolve_graph_npm_info(&graph)
|
||||
.package_reqs
|
||||
.into_iter()
|
||||
.map(|r| r.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
@ -95,59 +95,51 @@ impl NpmPackageResolver {
|
|||
no_npm: bool,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
) -> Self {
|
||||
Self::new_with_maybe_snapshot(
|
||||
Self::new_inner(cache, api, no_npm, local_node_modules_path, None, None)
|
||||
}
|
||||
|
||||
pub async fn new_with_maybe_lockfile(
|
||||
cache: NpmCache,
|
||||
api: RealNpmRegistryApi,
|
||||
no_npm: bool,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
) -> Result<Self, AnyError> {
|
||||
let maybe_snapshot = if let Some(lockfile) = &maybe_lockfile {
|
||||
if lockfile.lock().overwrite {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &api)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed reading lockfile '{}'",
|
||||
lockfile.lock().filename.display()
|
||||
)
|
||||
})?,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self::new_inner(
|
||||
cache,
|
||||
api,
|
||||
no_npm,
|
||||
local_node_modules_path,
|
||||
None,
|
||||
)
|
||||
maybe_snapshot,
|
||||
maybe_lockfile,
|
||||
))
|
||||
}
|
||||
|
||||
/// This function will replace current resolver with a new one built from a
|
||||
/// snapshot created out of the lockfile.
|
||||
pub async fn add_lockfile_and_maybe_regenerate_snapshot(
|
||||
&mut self,
|
||||
lockfile: Arc<Mutex<Lockfile>>,
|
||||
) -> Result<(), AnyError> {
|
||||
self.maybe_lockfile = Some(lockfile.clone());
|
||||
|
||||
if lockfile.lock().overwrite {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let snapshot =
|
||||
NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &self.api)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed reading lockfile '{}'",
|
||||
lockfile.lock().filename.display()
|
||||
)
|
||||
})?;
|
||||
if let Some(node_modules_folder) = &self.local_node_modules_path {
|
||||
self.inner = Arc::new(LocalNpmPackageResolver::new(
|
||||
self.cache.clone(),
|
||||
self.api.clone(),
|
||||
node_modules_folder.clone(),
|
||||
Some(snapshot),
|
||||
));
|
||||
} else {
|
||||
self.inner = Arc::new(GlobalNpmPackageResolver::new(
|
||||
self.cache.clone(),
|
||||
self.api.clone(),
|
||||
Some(snapshot),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_with_maybe_snapshot(
|
||||
fn new_inner(
|
||||
cache: NpmCache,
|
||||
api: RealNpmRegistryApi,
|
||||
no_npm: bool,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
initial_snapshot: Option<NpmResolutionSnapshot>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
) -> Self {
|
||||
let process_npm_state = NpmProcessState::take();
|
||||
let local_node_modules_path = local_node_modules_path.or_else(|| {
|
||||
|
@ -177,7 +169,7 @@ impl NpmPackageResolver {
|
|||
local_node_modules_path,
|
||||
api,
|
||||
cache,
|
||||
maybe_lockfile: None,
|
||||
maybe_lockfile,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,12 +312,13 @@ impl NpmPackageResolver {
|
|||
|
||||
/// Gets a new resolver with a new snapshotted state.
|
||||
pub fn snapshotted(&self) -> Self {
|
||||
Self::new_with_maybe_snapshot(
|
||||
Self::new_inner(
|
||||
self.cache.clone(),
|
||||
self.api.clone(),
|
||||
self.no_npm,
|
||||
self.local_node_modules_path.clone(),
|
||||
Some(self.snapshot()),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -336,6 +329,19 @@ impl NpmPackageResolver {
|
|||
pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
self.inner.lock(lockfile)
|
||||
}
|
||||
|
||||
pub async fn inject_synthetic_types_node_package(
|
||||
&self,
|
||||
) -> Result<(), AnyError> {
|
||||
// add and ensure this isn't added to the lockfile
|
||||
self
|
||||
.inner
|
||||
.add_package_reqs(vec![NpmPackageReq::from_str("@types/node").unwrap()])
|
||||
.await?;
|
||||
self.inner.cache_packages().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireNpmResolver for NpmPackageResolver {
|
||||
|
|
|
@ -23,7 +23,7 @@ use crate::graph_util::ModuleEntry;
|
|||
use crate::http_util::HttpClient;
|
||||
use crate::node;
|
||||
use crate::node::NodeResolution;
|
||||
use crate::npm::resolve_npm_package_reqs;
|
||||
use crate::npm::resolve_graph_npm_info;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
|
@ -261,20 +261,16 @@ impl ProcState {
|
|||
http_client.clone(),
|
||||
progress_bar.clone(),
|
||||
);
|
||||
let maybe_lockfile = lockfile.as_ref().cloned();
|
||||
let mut npm_resolver = NpmPackageResolver::new(
|
||||
let npm_resolver = NpmPackageResolver::new_with_maybe_lockfile(
|
||||
npm_cache.clone(),
|
||||
api,
|
||||
cli_options.no_npm(),
|
||||
cli_options
|
||||
.resolve_local_node_modules_folder()
|
||||
.with_context(|| "Resolving local node_modules folder.")?,
|
||||
);
|
||||
if let Some(lockfile) = maybe_lockfile {
|
||||
npm_resolver
|
||||
.add_lockfile_and_maybe_regenerate_snapshot(lockfile)
|
||||
.await?;
|
||||
}
|
||||
lockfile.as_ref().cloned(),
|
||||
)
|
||||
.await?;
|
||||
let node_analysis_cache =
|
||||
NodeAnalysisCache::new(Some(dir.node_analysis_db_file_path()));
|
||||
|
||||
|
@ -424,7 +420,7 @@ impl ProcState {
|
|||
graph_data.entries().map(|(s, _)| s).cloned().collect()
|
||||
};
|
||||
|
||||
let npm_package_reqs = {
|
||||
let (npm_package_reqs, has_node_builtin_specifier) = {
|
||||
let mut graph_data = self.graph_data.write();
|
||||
graph_data.add_graph(&graph);
|
||||
let check_js = self.options.check_js();
|
||||
|
@ -435,7 +431,10 @@ impl ProcState {
|
|||
check_js,
|
||||
)
|
||||
.unwrap()?;
|
||||
graph_data.npm_package_reqs().clone()
|
||||
(
|
||||
graph_data.npm_package_reqs().clone(),
|
||||
graph_data.has_node_builtin_specifier(),
|
||||
)
|
||||
};
|
||||
|
||||
if !npm_package_reqs.is_empty() {
|
||||
|
@ -443,6 +442,15 @@ impl ProcState {
|
|||
self.prepare_node_std_graph().await?;
|
||||
}
|
||||
|
||||
if has_node_builtin_specifier
|
||||
&& self.options.type_check_mode() != TypeCheckMode::None
|
||||
{
|
||||
self
|
||||
.npm_resolver
|
||||
.inject_synthetic_types_node_package()
|
||||
.await?;
|
||||
}
|
||||
|
||||
drop(_pb_clear_guard);
|
||||
|
||||
// type check if necessary
|
||||
|
@ -614,6 +622,11 @@ impl ProcState {
|
|||
}
|
||||
}
|
||||
|
||||
// Built-in Node modules
|
||||
if let Some(module_name) = specifier.strip_prefix("node:") {
|
||||
return node::resolve_builtin_node_module(module_name);
|
||||
}
|
||||
|
||||
// FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL
|
||||
// and `Deno.core.evalContext` API. Ideally we should always have a referrer filled
|
||||
// but sadly that's not the case due to missing APIs in V8.
|
||||
|
@ -732,9 +745,20 @@ impl ProcState {
|
|||
.await;
|
||||
|
||||
// add the found npm package requirements to the npm resolver and cache them
|
||||
let npm_package_reqs = resolve_npm_package_reqs(&graph);
|
||||
if !npm_package_reqs.is_empty() {
|
||||
self.npm_resolver.add_package_reqs(npm_package_reqs).await?;
|
||||
let graph_npm_info = resolve_graph_npm_info(&graph);
|
||||
if !graph_npm_info.package_reqs.is_empty() {
|
||||
self
|
||||
.npm_resolver
|
||||
.add_package_reqs(graph_npm_info.package_reqs)
|
||||
.await?;
|
||||
}
|
||||
if graph_npm_info.has_node_builtin_specifier
|
||||
&& self.options.type_check_mode() != TypeCheckMode::None
|
||||
{
|
||||
self
|
||||
.npm_resolver
|
||||
.inject_synthetic_types_node_package()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(graph)
|
||||
|
|
|
@ -64,6 +64,18 @@ itest!(check_static_response_json {
|
|||
exit_code: 0,
|
||||
});
|
||||
|
||||
itest!(check_node_builtin_modules_ts {
|
||||
args: "check --quiet check/node_builtin_modules/mod.ts",
|
||||
output: "check/node_builtin_modules/mod.ts.out",
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(check_node_builtin_modules_js {
|
||||
args: "check --quiet check/node_builtin_modules/mod.js",
|
||||
output: "check/node_builtin_modules/mod.js.out",
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(check_no_error_truncation {
|
||||
args: "check --quiet check/no_error_truncation/main.ts --config check/no_error_truncation/deno.json",
|
||||
output: "check/no_error_truncation/main.out",
|
||||
|
|
|
@ -4407,6 +4407,190 @@ fn lsp_npm_specifier_unopened_file() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_node_specifier() {
|
||||
let _g = http_server();
|
||||
let mut client = init("initialize_params.json");
|
||||
let diagnostics = CollectedDiagnostics(did_open(
|
||||
&mut client,
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import fs from 'node:non-existent';\n\n",
|
||||
}
|
||||
}),
|
||||
));
|
||||
|
||||
let non_existent_diagnostics = diagnostics
|
||||
.with_file_and_source("file:///a/file.ts", "deno")
|
||||
.diagnostics
|
||||
.into_iter()
|
||||
.filter(|d| {
|
||||
d.code == Some(lsp::NumberOrString::String("resolver-error".to_string()))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
json!(non_existent_diagnostics),
|
||||
json!([
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 15
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 34
|
||||
}
|
||||
},
|
||||
"severity": 1,
|
||||
"code": "resolver-error",
|
||||
"source": "deno",
|
||||
"message": "Unknown Node built-in module: non-existent"
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
// update to have node:fs import
|
||||
client
|
||||
.write_notification(
|
||||
"textDocument/didChange",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"version": 2
|
||||
},
|
||||
"contentChanges": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 16
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 33
|
||||
}
|
||||
},
|
||||
"text": "node:fs"
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
let diagnostics = read_diagnostics(&mut client);
|
||||
let cache_diagnostics = diagnostics
|
||||
.with_file_and_source("file:///a/file.ts", "deno")
|
||||
.diagnostics
|
||||
.into_iter()
|
||||
.filter(|d| {
|
||||
d.code == Some(lsp::NumberOrString::String("no-cache-npm".to_string()))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
json!(cache_diagnostics),
|
||||
json!([
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 15
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 24
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"specifier": "npm:@types/node",
|
||||
},
|
||||
"severity": 1,
|
||||
"code": "no-cache-npm",
|
||||
"source": "deno",
|
||||
"message": "Uncached or missing npm package: \"@types/node\"."
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request::<_, _, Value>(
|
||||
"deno/cache",
|
||||
json!({
|
||||
"referrer": {
|
||||
"uri": "file:///a/file.ts",
|
||||
},
|
||||
"uris": [
|
||||
{
|
||||
"uri": "npm:@types/node",
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert!(maybe_res.is_some());
|
||||
|
||||
client
|
||||
.write_notification(
|
||||
"textDocument/didChange",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"version": 2
|
||||
},
|
||||
"contentChanges": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"character": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"character": 0
|
||||
}
|
||||
},
|
||||
"text": "fs."
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
read_diagnostics(&mut client);
|
||||
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"textDocument/completion",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts"
|
||||
},
|
||||
"position": {
|
||||
"line": 2,
|
||||
"character": 3
|
||||
},
|
||||
"context": {
|
||||
"triggerKind": 2,
|
||||
"triggerCharacter": "."
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
|
||||
assert!(!list.is_incomplete);
|
||||
assert!(list.items.iter().any(|i| i.label == "writeFile"));
|
||||
assert!(list.items.iter().any(|i| i.label == "writeFileSync"));
|
||||
} else {
|
||||
panic!("unexpected response");
|
||||
}
|
||||
|
||||
shutdown(&mut client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_registry() {
|
||||
let _g = http_server();
|
||||
|
|
|
@ -3743,3 +3743,23 @@ fn stdio_streams_are_locked_in_permission_prompt() {
|
|||
assert_eq!(output, expected_output);
|
||||
});
|
||||
}
|
||||
|
||||
itest!(run_node_builtin_modules_ts {
|
||||
args: "run --quiet run/node_builtin_modules/mod.ts",
|
||||
output: "run/node_builtin_modules/mod.ts.out",
|
||||
envs: vec![(
|
||||
"DENO_NODE_COMPAT_URL".to_string(),
|
||||
test_util::std_file_url()
|
||||
)],
|
||||
exit_code: 0,
|
||||
});
|
||||
|
||||
itest!(run_node_builtin_modules_js {
|
||||
args: "run --quiet run/node_builtin_modules/mod.js",
|
||||
output: "run/node_builtin_modules/mod.js.out",
|
||||
envs: vec![(
|
||||
"DENO_NODE_COMPAT_URL".to_string(),
|
||||
test_util::std_file_url()
|
||||
)],
|
||||
exit_code: 0,
|
||||
});
|
||||
|
|
3
cli/tests/testdata/check/node_builtin_modules/mod.js
vendored
Normal file
3
cli/tests/testdata/check/node_builtin_modules/mod.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
// @ts-check
|
||||
import fs from "node:fs";
|
||||
const _data = fs.readFileSync("./node_builtin.js", 123);
|
5
cli/tests/testdata/check/node_builtin_modules/mod.js.out
vendored
Normal file
5
cli/tests/testdata/check/node_builtin_modules/mod.js.out
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: TS2769 [ERROR]: No overload matches this call.
|
||||
[WILDCARD]
|
||||
const _data = fs.readFileSync("./node_builtin.js", 123);
|
||||
~~~
|
||||
at file:///[WILDCARD]/node_builtin_modules/mod.js:3:52
|
9
cli/tests/testdata/check/node_builtin_modules/mod.ts
vendored
Normal file
9
cli/tests/testdata/check/node_builtin_modules/mod.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
import fs from "node:fs";
|
||||
const _data = fs.readFileSync("./node_builtin.js", 123);
|
||||
|
||||
// check node:module specifically because for deno check it should
|
||||
// resolve to the @types/node package, but at runtime it uses a different
|
||||
// builtin object than deno_std
|
||||
import { builtinModules } from "node:module";
|
||||
// should error about being string[]
|
||||
const _testString: number[] = builtinModules;
|
13
cli/tests/testdata/check/node_builtin_modules/mod.ts.out
vendored
Normal file
13
cli/tests/testdata/check/node_builtin_modules/mod.ts.out
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
error: TS2769 [ERROR]: No overload matches this call.
|
||||
[WILDCARD]
|
||||
const _data = fs.readFileSync("./node_builtin.js", 123);
|
||||
~~~
|
||||
at file:///[WILDCARD]/node_builtin_modules/mod.ts:2:52
|
||||
|
||||
TS2322 [ERROR]: Type 'string[]' is not assignable to type 'number[]'.
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
const _testString: number[] = builtinModules;
|
||||
~~~~~~~~~~~
|
||||
at file:///[WILDCARD]/node_builtin_modules/mod.ts:9:7
|
||||
|
||||
Found 2 errors.
|
2
cli/tests/testdata/run/node_builtin_modules/mod.js
vendored
Normal file
2
cli/tests/testdata/run/node_builtin_modules/mod.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
import process from "node:process";
|
||||
console.log(process.version);
|
1
cli/tests/testdata/run/node_builtin_modules/mod.js.out
vendored
Normal file
1
cli/tests/testdata/run/node_builtin_modules/mod.js.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
v[WILDCARD].[WILDCARD].[WILDCARD]
|
2
cli/tests/testdata/run/node_builtin_modules/mod.ts
vendored
Normal file
2
cli/tests/testdata/run/node_builtin_modules/mod.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
import process from "node:process";
|
||||
console.log(process.version);
|
1
cli/tests/testdata/run/node_builtin_modules/mod.ts.out
vendored
Normal file
1
cli/tests/testdata/run/node_builtin_modules/mod.ts.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
v[WILDCARD].[WILDCARD].[WILDCARD]
|
|
@ -232,10 +232,16 @@ fn get_tsc_roots(
|
|||
graph_data: &GraphData,
|
||||
check_js: bool,
|
||||
) -> Vec<(ModuleSpecifier, MediaType)> {
|
||||
graph_data
|
||||
.entries()
|
||||
.into_iter()
|
||||
.filter_map(|(specifier, module_entry)| match module_entry {
|
||||
let mut result = Vec::new();
|
||||
if graph_data.has_node_builtin_specifier() {
|
||||
// inject a specifier that will resolve node types
|
||||
result.push((
|
||||
ModuleSpecifier::parse("asset:///node_types.d.ts").unwrap(),
|
||||
MediaType::Dts,
|
||||
));
|
||||
}
|
||||
result.extend(graph_data.entries().into_iter().filter_map(
|
||||
|(specifier, module_entry)| match module_entry {
|
||||
ModuleEntry::Module {
|
||||
media_type, code, ..
|
||||
} => match media_type {
|
||||
|
@ -252,8 +258,9 @@ fn get_tsc_roots(
|
|||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
));
|
||||
result
|
||||
}
|
||||
|
||||
/// Matches the `@ts-check` pragma.
|
||||
|
|
15
cli/tsc/00_typescript.js
vendored
15
cli/tsc/00_typescript.js
vendored
|
@ -91389,10 +91389,15 @@ var ts;
|
|||
var deno;
|
||||
(function (deno) {
|
||||
var isNodeSourceFile = function () { return false; };
|
||||
var nodeBuiltInModuleNames = new ts.Set();
|
||||
function setIsNodeSourceFileCallback(callback) {
|
||||
isNodeSourceFile = callback;
|
||||
}
|
||||
deno.setIsNodeSourceFileCallback = setIsNodeSourceFileCallback;
|
||||
function setNodeBuiltInModuleNames(names) {
|
||||
nodeBuiltInModuleNames = new ts.Set(names);
|
||||
}
|
||||
deno.setNodeBuiltInModuleNames = setNodeBuiltInModuleNames;
|
||||
// When upgrading:
|
||||
// 1. Inspect all usages of "globals" and "globalThisSymbol" in checker.ts
|
||||
// - Beware that `globalThisType` might refer to the global `this` type
|
||||
|
@ -91452,8 +91457,16 @@ var ts;
|
|||
function getGlobalsForName(id) {
|
||||
// Node ambient modules are only accessible in the node code,
|
||||
// so put them on the node globals
|
||||
if (ambientModuleSymbolRegex.test(id))
|
||||
if (ambientModuleSymbolRegex.test(id)) {
|
||||
if (id.startsWith('"node:')) {
|
||||
// check if it's a node specifier that we support
|
||||
var name = id.slice(6, -1);
|
||||
if (nodeBuiltInModuleNames.has(name)) {
|
||||
return globals;
|
||||
}
|
||||
}
|
||||
return nodeGlobals;
|
||||
}
|
||||
return nodeOnlyGlobalNames.has(id) ? nodeGlobals : globals;
|
||||
}
|
||||
function mergeGlobalSymbolTable(node, source, unidirectional) {
|
||||
|
|
|
@ -855,25 +855,7 @@ delete Object.prototype.__proto__;
|
|||
...program.getOptionsDiagnostics(),
|
||||
...program.getGlobalDiagnostics(),
|
||||
...program.getSemanticDiagnostics(),
|
||||
].filter((diagnostic) => {
|
||||
if (IGNORED_DIAGNOSTICS.includes(diagnostic.code)) {
|
||||
return false;
|
||||
} else if (
|
||||
diagnostic.code === 1259 &&
|
||||
typeof diagnostic.messageText === "string" &&
|
||||
diagnostic.messageText.startsWith(
|
||||
"Module '\"deno:///missing_dependency.d.ts\"' can only be default-imported using the 'allowSyntheticDefaultImports' flag",
|
||||
)
|
||||
) {
|
||||
// For now, ignore diagnostics like:
|
||||
// > TS1259 [ERROR]: Module '"deno:///missing_dependency.d.ts"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
|
||||
// This diagnostic has surfaced due to supporting node cjs imports because this module does `export =`.
|
||||
// See discussion in https://github.com/microsoft/TypeScript/pull/51136
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
].filter((diagnostic) => !IGNORED_DIAGNOSTICS.includes(diagnostic.code));
|
||||
|
||||
// emit the tsbuildinfo file
|
||||
// @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871)
|
||||
|
@ -1273,9 +1255,11 @@ delete Object.prototype.__proto__;
|
|||
|
||||
// A build time only op that provides some setup information that is used to
|
||||
// ensure the snapshot is setup properly.
|
||||
/** @type {{ buildSpecifier: string; libs: string[] }} */
|
||||
/** @type {{ buildSpecifier: string; libs: string[]; nodeBuiltInModuleNames: string[] }} */
|
||||
const { buildSpecifier, libs, nodeBuiltInModuleNames } = ops.op_build_info();
|
||||
|
||||
ts.deno.setNodeBuiltInModuleNames(nodeBuiltInModuleNames);
|
||||
|
||||
const { buildSpecifier, libs } = ops.op_build_info();
|
||||
for (const lib of libs) {
|
||||
const specifier = `lib.${lib}.d.ts`;
|
||||
// we are using internal APIs here to "inject" our custom libraries into
|
||||
|
|
1
cli/tsc/compiler.d.ts
vendored
1
cli/tsc/compiler.d.ts
vendored
|
@ -30,6 +30,7 @@ declare global {
|
|||
function setIsNodeSourceFileCallback(
|
||||
callback: (sourceFile: SourceFile) => boolean,
|
||||
);
|
||||
function setNodeBuiltInModuleNames(names: string[]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -299,9 +299,11 @@ impl Diagnostic {
|
|||
|
||||
fn fmt_related_information(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(related_information) = self.related_information.as_ref() {
|
||||
write!(f, "\n\n")?;
|
||||
for info in related_information {
|
||||
info.fmt_stack(f, 4)?;
|
||||
if !related_information.is_empty() {
|
||||
write!(f, "\n\n")?;
|
||||
for info in related_information {
|
||||
info.fmt_stack(f, 4)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
214
cli/tsc/mod.rs
214
cli/tsc/mod.rs
|
@ -165,6 +165,12 @@ pub static LAZILY_LOADED_STATIC_ASSETS: Lazy<
|
|||
"lib.webworker.iterable.d.ts",
|
||||
inc!("lib.webworker.iterable.d.ts"),
|
||||
),
|
||||
(
|
||||
// Special file that can be used to inject the @types/node package.
|
||||
// This is used for `node:` specifiers.
|
||||
"node_types.d.ts",
|
||||
"/// <reference types=\"npm:@types/node\" />\n",
|
||||
),
|
||||
])
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -599,117 +605,133 @@ fn op_resolve(
|
|||
"Error converting a string module specifier for \"op_resolve\".",
|
||||
)?
|
||||
};
|
||||
for specifier in &args.specifiers {
|
||||
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() {
|
||||
// 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
|
||||
resolved.push((specifier, MediaType::Dts.to_string()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if specifier.starts_with("asset:///") {
|
||||
resolved.push((
|
||||
specifier.clone(),
|
||||
MediaType::from(specifier).as_ts_extension().to_string(),
|
||||
));
|
||||
} else {
|
||||
let graph_data = state.graph_data.read();
|
||||
let resolved_dep = match graph_data.get_dependencies(&referrer) {
|
||||
Some(dependencies) => dependencies.get(specifier).map(|d| {
|
||||
if matches!(d.maybe_type, Resolved::Ok { .. }) {
|
||||
&d.maybe_type
|
||||
} else {
|
||||
&d.maybe_code
|
||||
}
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
let maybe_result = match resolved_dep {
|
||||
Some(Resolved::Ok { specifier, .. }) => {
|
||||
let specifier = graph_data.follow_redirect(specifier);
|
||||
match graph_data.get(&specifier) {
|
||||
Some(ModuleEntry::Module {
|
||||
media_type,
|
||||
maybe_types,
|
||||
..
|
||||
}) => match maybe_types {
|
||||
Some(Resolved::Ok { specifier, .. }) => {
|
||||
let types = graph_data.follow_redirect(specifier);
|
||||
match graph_data.get(&types) {
|
||||
Some(ModuleEntry::Module { media_type, .. }) => {
|
||||
Some((types, *media_type))
|
||||
}
|
||||
_ => None,
|
||||
let media_type =
|
||||
MediaType::from(&specifier).as_ts_extension().to_string();
|
||||
resolved.push((specifier, media_type));
|
||||
continue;
|
||||
}
|
||||
|
||||
let graph_data = state.graph_data.read();
|
||||
let resolved_dep = match graph_data.get_dependencies(&referrer) {
|
||||
Some(dependencies) => dependencies.get(&specifier).map(|d| {
|
||||
if matches!(d.maybe_type, Resolved::Ok { .. }) {
|
||||
&d.maybe_type
|
||||
} else {
|
||||
&d.maybe_code
|
||||
}
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
let maybe_result = match resolved_dep {
|
||||
Some(Resolved::Ok { specifier, .. }) => {
|
||||
let specifier = graph_data.follow_redirect(specifier);
|
||||
match graph_data.get(&specifier) {
|
||||
Some(ModuleEntry::Module {
|
||||
media_type,
|
||||
maybe_types,
|
||||
..
|
||||
}) => match maybe_types {
|
||||
Some(Resolved::Ok { specifier, .. }) => {
|
||||
let types = graph_data.follow_redirect(specifier);
|
||||
match graph_data.get(&types) {
|
||||
Some(ModuleEntry::Module { media_type, .. }) => {
|
||||
Some((types, *media_type))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
_ => Some((specifier, *media_type)),
|
||||
},
|
||||
_ => {
|
||||
// handle npm:<package> urls
|
||||
if let Ok(npm_ref) =
|
||||
NpmPackageReference::from_specifier(&specifier)
|
||||
{
|
||||
if let Some(npm_resolver) = &state.maybe_npm_resolver {
|
||||
Some(resolve_npm_package_reference_types(
|
||||
&npm_ref,
|
||||
npm_resolver,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => Some((specifier, *media_type)),
|
||||
},
|
||||
_ => {
|
||||
// handle npm:<package> urls
|
||||
if let Ok(npm_ref) = NpmPackageReference::from_specifier(&specifier)
|
||||
{
|
||||
if let Some(npm_resolver) = &state.maybe_npm_resolver {
|
||||
Some(resolve_npm_package_reference_types(
|
||||
&npm_ref,
|
||||
npm_resolver,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
state.maybe_npm_resolver.as_ref().and_then(|npm_resolver| {
|
||||
if npm_resolver.in_npm_package(&referrer) {
|
||||
// we're in an npm package, so use node resolution
|
||||
Some(NodeResolution::into_specifier_and_media_type(
|
||||
node::node_resolve(
|
||||
specifier,
|
||||
&referrer,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
&mut PermissionsContainer::allow_all(),
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
let result = match maybe_result {
|
||||
Some((specifier, media_type)) => {
|
||||
let specifier_str = match specifier.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = hash_url(&specifier, media_type);
|
||||
}
|
||||
_ => {
|
||||
if let Some(npm_resolver) = state.maybe_npm_resolver.as_ref() {
|
||||
if npm_resolver.in_npm_package(&referrer) {
|
||||
// we're in an npm package, so use node resolution
|
||||
Some(NodeResolution::into_specifier_and_media_type(
|
||||
node::node_resolve(
|
||||
&specifier,
|
||||
&referrer,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
&mut PermissionsContainer::allow_all(),
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
))
|
||||
} else if let Ok(npm_ref) = NpmPackageReference::from_str(&specifier)
|
||||
{
|
||||
// this could occur when resolving npm:@types/node when it is
|
||||
// injected and not part of the graph
|
||||
Some(resolve_npm_package_reference_types(&npm_ref, npm_resolver)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
let result = match maybe_result {
|
||||
Some((specifier, media_type)) => {
|
||||
let specifier_str = match specifier.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = hash_url(&specifier, media_type);
|
||||
state
|
||||
.remapped_specifiers
|
||||
.insert(specifier_str.clone(), specifier);
|
||||
specifier_str
|
||||
}
|
||||
_ => {
|
||||
if let Some(specifier_str) =
|
||||
maybe_remap_specifier(&specifier, media_type)
|
||||
{
|
||||
state
|
||||
.remapped_specifiers
|
||||
.insert(specifier_str.clone(), specifier);
|
||||
specifier_str
|
||||
} else {
|
||||
specifier.to_string()
|
||||
}
|
||||
_ => {
|
||||
if let Some(specifier_str) =
|
||||
maybe_remap_specifier(&specifier, media_type)
|
||||
{
|
||||
state
|
||||
.remapped_specifiers
|
||||
.insert(specifier_str.clone(), specifier);
|
||||
specifier_str
|
||||
} else {
|
||||
specifier.to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
(specifier_str, media_type.as_ts_extension().into())
|
||||
}
|
||||
None => (
|
||||
"deno:///missing_dependency.d.ts".to_string(),
|
||||
".d.ts".to_string(),
|
||||
),
|
||||
};
|
||||
log::debug!("Resolved {} to {:?}", specifier, result);
|
||||
resolved.push(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
(specifier_str, media_type.as_ts_extension().into())
|
||||
}
|
||||
None => (
|
||||
"deno:///missing_dependency.d.ts".to_string(),
|
||||
".d.ts".to_string(),
|
||||
),
|
||||
};
|
||||
log::debug!("Resolved {} to {:?}", specifier, result);
|
||||
resolved.push(result);
|
||||
}
|
||||
|
||||
Ok(resolved)
|
||||
|
|
194
ext/node/lib.rs
194
ext/node/lib.rs
|
@ -675,3 +675,197 @@ fn op_require_break_on_next_statement(state: &mut OpState) {
|
|||
.borrow_mut()
|
||||
.wait_for_session_and_break_on_next_statement()
|
||||
}
|
||||
|
||||
pub struct NodeModulePolyfill {
|
||||
/// Name of the module like "assert" or "timers/promises"
|
||||
pub name: &'static str,
|
||||
|
||||
/// Specifier relative to the root of `deno_std` repo, like "node/assert.ts"
|
||||
pub specifier: &'static str,
|
||||
}
|
||||
|
||||
pub static SUPPORTED_BUILTIN_NODE_MODULES: &[NodeModulePolyfill] = &[
|
||||
NodeModulePolyfill {
|
||||
name: "assert",
|
||||
specifier: "node/assert.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "assert/strict",
|
||||
specifier: "node/assert/strict.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "async_hooks",
|
||||
specifier: "node/async_hooks.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "buffer",
|
||||
specifier: "node/buffer.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "child_process",
|
||||
specifier: "node/child_process.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "cluster",
|
||||
specifier: "node/cluster.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "console",
|
||||
specifier: "node/console.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "constants",
|
||||
specifier: "node/constants.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "crypto",
|
||||
specifier: "node/crypto.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dgram",
|
||||
specifier: "node/dgram.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dns",
|
||||
specifier: "node/dns.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "dns/promises",
|
||||
specifier: "node/dns/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "domain",
|
||||
specifier: "node/domain.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "events",
|
||||
specifier: "node/events.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "fs",
|
||||
specifier: "node/fs.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "fs/promises",
|
||||
specifier: "node/fs/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "http",
|
||||
specifier: "node/http.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "https",
|
||||
specifier: "node/https.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "module",
|
||||
// NOTE(bartlomieju): `module` is special, because we don't want to use
|
||||
// `deno_std/node/module.ts`, but instead use a special shim that we
|
||||
// provide in `ext/node`.
|
||||
specifier: "[USE `deno_node::MODULE_ES_SHIM` to get this module]",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "net",
|
||||
specifier: "node/net.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "os",
|
||||
specifier: "node/os.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path",
|
||||
specifier: "node/path.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path/posix",
|
||||
specifier: "node/path/posix.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "path/win32",
|
||||
specifier: "node/path/win32.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "perf_hooks",
|
||||
specifier: "node/perf_hooks.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "process",
|
||||
specifier: "node/process.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "querystring",
|
||||
specifier: "node/querystring.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "readline",
|
||||
specifier: "node/readline.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream",
|
||||
specifier: "node/stream.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/consumers",
|
||||
specifier: "node/stream/consumers.mjs",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/promises",
|
||||
specifier: "node/stream/promises.mjs",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "stream/web",
|
||||
specifier: "node/stream/web.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "string_decoder",
|
||||
specifier: "node/string_decoder.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "sys",
|
||||
specifier: "node/sys.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "timers",
|
||||
specifier: "node/timers.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "timers/promises",
|
||||
specifier: "node/timers/promises.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "tls",
|
||||
specifier: "node/tls.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "tty",
|
||||
specifier: "node/tty.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "url",
|
||||
specifier: "node/url.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "util",
|
||||
specifier: "node/util.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "util/types",
|
||||
specifier: "node/util/types.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "v8",
|
||||
specifier: "node/v8.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "vm",
|
||||
specifier: "node/vm.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "worker_threads",
|
||||
specifier: "node/worker_threads.ts",
|
||||
},
|
||||
NodeModulePolyfill {
|
||||
name: "zlib",
|
||||
specifier: "node/zlib.ts",
|
||||
},
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue