1#![deny(clippy::print_stderr)]
4#![deny(clippy::print_stdout)]
5
6use std::borrow::Cow;
7use std::path::PathBuf;
8
9use boxed_error::Boxed;
10use deno_cache_dir::npm::NpmCacheDir;
11use deno_error::JsError;
12use deno_package_json::PackageJsonDepValue;
13use deno_package_json::PackageJsonDepValueParseError;
14use deno_semver::npm::NpmPackageReqReference;
15pub use node_resolver::DenoIsBuiltInNodeModuleChecker;
16use node_resolver::InNpmPackageChecker;
17use node_resolver::IsBuiltInNodeModuleChecker;
18use node_resolver::NodeResolution;
19use node_resolver::NodeResolutionKind;
20pub use node_resolver::NodeResolverOptions;
21use node_resolver::NodeResolverRc;
22use node_resolver::NpmPackageFolderResolver;
23use node_resolver::ResolutionMode;
24use node_resolver::UrlOrPath;
25use node_resolver::UrlOrPathRef;
26use node_resolver::errors::NodeJsErrorCode;
27use node_resolver::errors::NodeResolveError;
28use node_resolver::errors::NodeResolveErrorKind;
29use node_resolver::errors::UnknownBuiltInNodeModuleError;
30use npm::NodeModulesOutOfDateError;
31use npm::NpmReqResolverRc;
32use npm::ResolveIfForNpmPackageErrorKind;
33use npm::ResolvePkgFolderFromDenoReqError;
34use thiserror::Error;
35use url::Url;
36
37use self::npm::NpmResolver;
38use self::npm::NpmResolverSys;
39use self::npm::ResolveNpmReqRefError;
40use crate::workspace::MappedResolution;
41use crate::workspace::MappedResolutionDiagnostic;
42use crate::workspace::MappedResolutionError;
43use crate::workspace::WorkspaceResolvePkgJsonFolderError;
44use crate::workspace::WorkspaceResolver;
45
46pub mod cache;
47pub mod cjs;
48pub mod collections;
49pub mod deno_json;
50pub mod display;
51#[cfg(feature = "deno_ast")]
52pub mod emit;
53pub mod factory;
54#[cfg(feature = "graph")]
55pub mod file_fetcher;
56#[cfg(feature = "graph")]
57pub mod graph;
58pub mod import_map;
59pub mod loader;
60pub mod lockfile;
61pub mod npm;
62pub mod npmrc;
63#[cfg(feature = "sync")]
64mod rt;
65pub mod workspace;
66
67#[allow(clippy::disallowed_types)]
68pub type WorkspaceResolverRc<TSys> =
69 deno_maybe_sync::MaybeArc<WorkspaceResolver<TSys>>;
70
71#[allow(clippy::disallowed_types)]
72pub(crate) type NpmCacheDirRc = deno_maybe_sync::MaybeArc<NpmCacheDir>;
73
74#[derive(Debug, Clone)]
75pub struct DenoResolution {
76 pub url: Url,
77 pub maybe_diagnostic: Option<Box<MappedResolutionDiagnostic>>,
78 pub found_package_json_dep: bool,
79}
80
81#[derive(Debug, Boxed, JsError)]
82pub struct DenoResolveError(pub Box<DenoResolveErrorKind>);
83
84impl DenoResolveError {
85 #[cfg(feature = "graph")]
86 pub fn into_deno_graph_error(self) -> deno_graph::source::ResolveError {
87 use deno_error::JsErrorBox;
88 use deno_graph::source::ResolveError;
89
90 match self.into_kind() {
91 DenoResolveErrorKind::MappedResolution(mapped_resolution_error) => {
92 match mapped_resolution_error {
93 MappedResolutionError::Specifier(e) => ResolveError::Specifier(e),
94 MappedResolutionError::ImportMap(e) => ResolveError::ImportMap(e),
96 MappedResolutionError::Workspace(e) => {
97 ResolveError::Other(JsErrorBox::from_err(e))
98 }
99 MappedResolutionError::NotFoundInCompilerOptionsPaths(e) => {
100 ResolveError::Other(JsErrorBox::from_err(e))
101 }
102 }
103 }
104 err => ResolveError::Other(JsErrorBox::from_err(err)),
105 }
106 }
107
108 pub fn maybe_specifier(&self) -> Option<Cow<'_, UrlOrPath>> {
109 match self.as_kind() {
110 DenoResolveErrorKind::Node(err) => err.maybe_specifier(),
111 DenoResolveErrorKind::PathToUrl(err) => {
112 Some(Cow::Owned(UrlOrPath::Path(err.0.clone())))
113 }
114 DenoResolveErrorKind::ResolveNpmReqRef(err) => err.err.maybe_specifier(),
115 DenoResolveErrorKind::MappedResolution(_)
116 | DenoResolveErrorKind::WorkspaceResolvePkgJsonFolder(_)
117 | DenoResolveErrorKind::ResolvePkgFolderFromDenoReq(_)
118 | DenoResolveErrorKind::InvalidVendorFolderImport
119 | DenoResolveErrorKind::UnsupportedPackageJsonFileSpecifier
120 | DenoResolveErrorKind::UnsupportedPackageJsonJsrReq
121 | DenoResolveErrorKind::NodeModulesOutOfDate(_)
122 | DenoResolveErrorKind::PackageJsonDepValueParse(_)
123 | DenoResolveErrorKind::PackageJsonDepValueUrlParse(_) => None,
124 }
125 }
126}
127
128#[derive(Debug, Error, JsError)]
129pub enum DenoResolveErrorKind {
130 #[class(type)]
131 #[error(
132 "Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring."
133 )]
134 InvalidVendorFolderImport,
135 #[class(type)]
136 #[error(
137 "Importing npm packages via a file: specifier is only supported with --node-modules-dir=manual"
138 )]
139 UnsupportedPackageJsonFileSpecifier,
140 #[class(type)]
141 #[error("JSR specifiers are not yet supported in package.json")]
142 UnsupportedPackageJsonJsrReq,
143 #[class(inherit)]
144 #[error(transparent)]
145 MappedResolution(#[from] MappedResolutionError),
146 #[class(inherit)]
147 #[error(transparent)]
148 Node(#[from] NodeResolveError),
149 #[class(inherit)]
150 #[error(transparent)]
151 NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
152 #[class(inherit)]
153 #[error(transparent)]
154 PackageJsonDepValueParse(#[from] PackageJsonDepValueParseError),
155 #[class(inherit)]
156 #[error(transparent)]
157 PackageJsonDepValueUrlParse(url::ParseError),
158 #[class(inherit)]
159 #[error(transparent)]
160 PathToUrl(#[from] deno_path_util::PathToUrlError),
161 #[class(inherit)]
162 #[error(transparent)]
163 ResolveNpmReqRef(#[from] ResolveNpmReqRefError),
164 #[class(inherit)]
165 #[error(transparent)]
166 ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
167 #[class(inherit)]
168 #[error(transparent)]
169 WorkspaceResolvePkgJsonFolder(#[from] WorkspaceResolvePkgJsonFolderError),
170}
171
172impl DenoResolveErrorKind {
173 pub fn maybe_node_code(&self) -> Option<NodeJsErrorCode> {
174 match self {
175 DenoResolveErrorKind::InvalidVendorFolderImport
176 | DenoResolveErrorKind::UnsupportedPackageJsonFileSpecifier
177 | DenoResolveErrorKind::UnsupportedPackageJsonJsrReq
178 | DenoResolveErrorKind::MappedResolution { .. }
179 | DenoResolveErrorKind::NodeModulesOutOfDate { .. }
180 | DenoResolveErrorKind::PackageJsonDepValueParse { .. }
181 | DenoResolveErrorKind::PackageJsonDepValueUrlParse { .. }
182 | DenoResolveErrorKind::PathToUrl { .. }
183 | DenoResolveErrorKind::ResolvePkgFolderFromDenoReq { .. }
184 | DenoResolveErrorKind::WorkspaceResolvePkgJsonFolder { .. } => None,
185 DenoResolveErrorKind::ResolveNpmReqRef(err) => {
186 err.err.as_kind().maybe_code()
187 }
188 DenoResolveErrorKind::Node(err) => err.as_kind().maybe_code(),
189 }
190 }
191}
192
193#[derive(Debug)]
194pub struct NodeAndNpmResolvers<
195 TInNpmPackageChecker: InNpmPackageChecker,
196 TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
197 TNpmPackageFolderResolver: NpmPackageFolderResolver,
198 TSys: NpmResolverSys,
199> {
200 pub node_resolver: NodeResolverRc<
201 TInNpmPackageChecker,
202 TIsBuiltInNodeModuleChecker,
203 TNpmPackageFolderResolver,
204 TSys,
205 >,
206 pub npm_resolver: NpmResolver<TSys>,
207 pub npm_req_resolver: NpmReqResolverRc<
208 TInNpmPackageChecker,
209 TIsBuiltInNodeModuleChecker,
210 TNpmPackageFolderResolver,
211 TSys,
212 >,
213}
214
215#[sys_traits::auto_impl]
216pub trait DenoResolverSys: NpmResolverSys {}
217
218pub struct DenoResolverOptions<
219 'a,
220 TInNpmPackageChecker: InNpmPackageChecker,
221 TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
222 TNpmPackageFolderResolver: NpmPackageFolderResolver,
223 TSys: DenoResolverSys,
224> {
225 pub in_npm_pkg_checker: TInNpmPackageChecker,
226 pub node_and_req_resolver: Option<
227 NodeAndNpmResolvers<
228 TInNpmPackageChecker,
229 TIsBuiltInNodeModuleChecker,
230 TNpmPackageFolderResolver,
231 TSys,
232 >,
233 >,
234 pub workspace_resolver: WorkspaceResolverRc<TSys>,
235 pub bare_node_builtins: bool,
237 pub is_byonm: bool,
241 pub maybe_vendor_dir: Option<&'a PathBuf>,
242}
243
244#[allow(clippy::disallowed_types)]
245pub type RawDenoResolverRc<
246 TInNpmPackageChecker,
247 TIsBuiltInNodeModuleChecker,
248 TNpmPackageFolderResolver,
249 TSys,
250> = deno_maybe_sync::MaybeArc<
251 RawDenoResolver<
252 TInNpmPackageChecker,
253 TIsBuiltInNodeModuleChecker,
254 TNpmPackageFolderResolver,
255 TSys,
256 >,
257>;
258
259pub type DefaultRawDenoResolverRc<TSys> = RawDenoResolverRc<
262 npm::DenoInNpmPackageChecker,
263 DenoIsBuiltInNodeModuleChecker,
264 npm::NpmResolver<TSys>,
265 TSys,
266>;
267
268#[derive(Debug)]
271pub struct RawDenoResolver<
272 TInNpmPackageChecker: InNpmPackageChecker,
273 TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
274 TNpmPackageFolderResolver: NpmPackageFolderResolver,
275 TSys: DenoResolverSys,
276> {
277 in_npm_pkg_checker: TInNpmPackageChecker,
278 node_and_npm_resolver: Option<
279 NodeAndNpmResolvers<
280 TInNpmPackageChecker,
281 TIsBuiltInNodeModuleChecker,
282 TNpmPackageFolderResolver,
283 TSys,
284 >,
285 >,
286 workspace_resolver: WorkspaceResolverRc<TSys>,
287 bare_node_builtins: bool,
288 is_byonm: bool,
289 maybe_vendor_specifier: Option<Url>,
290}
291
292impl<
293 TInNpmPackageChecker: InNpmPackageChecker,
294 TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
295 TNpmPackageFolderResolver: NpmPackageFolderResolver,
296 TSys: DenoResolverSys,
297>
298 RawDenoResolver<
299 TInNpmPackageChecker,
300 TIsBuiltInNodeModuleChecker,
301 TNpmPackageFolderResolver,
302 TSys,
303 >
304{
305 pub fn new(
306 options: DenoResolverOptions<
307 TInNpmPackageChecker,
308 TIsBuiltInNodeModuleChecker,
309 TNpmPackageFolderResolver,
310 TSys,
311 >,
312 ) -> Self {
313 Self {
314 in_npm_pkg_checker: options.in_npm_pkg_checker,
315 node_and_npm_resolver: options.node_and_req_resolver,
316 workspace_resolver: options.workspace_resolver,
317 bare_node_builtins: options.bare_node_builtins,
318 is_byonm: options.is_byonm,
319 maybe_vendor_specifier: options
320 .maybe_vendor_dir
321 .and_then(|v| deno_path_util::url_from_directory_path(v).ok()),
322 }
323 }
324
325 pub fn resolve(
326 &self,
327 raw_specifier: &str,
328 referrer: &Url,
329 resolution_mode: ResolutionMode,
330 resolution_kind: NodeResolutionKind,
331 ) -> Result<DenoResolution, DenoResolveError> {
332 let mut found_package_json_dep = false;
333 let mut maybe_diagnostic = None;
334 if let Some(node_and_npm_resolver) = self.node_and_npm_resolver.as_ref() {
336 let node_resolver = &node_and_npm_resolver.node_resolver;
337 if referrer.scheme() == "file"
338 && self.in_npm_pkg_checker.in_npm_package(referrer)
339 {
340 log::debug!(
341 "{}: specifier={} referrer={} mode={:?} kind={:?}",
342 deno_terminal::colors::magenta("resolving in npm package"),
343 raw_specifier,
344 referrer,
345 resolution_mode,
346 resolution_kind
347 );
348 return node_resolver
349 .resolve(raw_specifier, referrer, resolution_mode, resolution_kind)
350 .and_then(|res| {
351 Ok(DenoResolution {
352 url: res.into_url()?,
353 found_package_json_dep,
354 maybe_diagnostic,
355 })
356 })
357 .map_err(|e| e.into());
358 }
359 }
360
361 let result = self.workspace_resolver.resolve(
363 raw_specifier,
364 referrer,
365 resolution_kind.into(),
366 );
367 let result = match result {
368 Ok(resolution) => match resolution {
369 MappedResolution::Normal {
370 specifier,
371 maybe_diagnostic: current_diagnostic,
372 ..
373 } => {
374 maybe_diagnostic = current_diagnostic;
375 Ok(specifier)
376 }
377 MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
378 Ok(specifier)
379 }
380 MappedResolution::WorkspaceNpmPackage {
381 target_pkg_json: pkg_json,
382 sub_path,
383 ..
384 } => self
385 .node_and_npm_resolver
386 .as_ref()
387 .unwrap()
388 .node_resolver
389 .resolve_package_subpath_from_deno_module(
390 pkg_json.dir_path(),
391 sub_path.as_deref(),
392 Some(referrer),
393 resolution_mode,
394 resolution_kind,
395 )
396 .map_err(|e| {
397 DenoResolveErrorKind::Node(e.into_node_resolve_error()).into_box()
398 })
399 .and_then(|r| Ok(r.into_url()?)),
400 MappedResolution::PackageJson {
401 dep_result,
402 alias,
403 sub_path,
404 ..
405 } => {
406 found_package_json_dep = true;
409
410 dep_result
411 .as_ref()
412 .map_err(|e| {
413 DenoResolveErrorKind::PackageJsonDepValueParse(e.clone())
414 .into_box()
415 })
416 .and_then(|dep| match dep {
417 PackageJsonDepValue::File(_) => {
418 Err(
423 DenoResolveErrorKind::UnsupportedPackageJsonFileSpecifier
424 .into_box(),
425 )
426 }
427 PackageJsonDepValue::JsrReq(_) => Err(
428 DenoResolveErrorKind::UnsupportedPackageJsonJsrReq.into_box(),
429 ),
430 PackageJsonDepValue::Req(req) => Url::parse(&format!(
433 "npm:{}{}",
434 req,
435 sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
436 ))
437 .map_err(|e| {
438 DenoResolveErrorKind::PackageJsonDepValueUrlParse(e).into_box()
439 }),
440 PackageJsonDepValue::Workspace(version_req) => self
441 .workspace_resolver
442 .resolve_workspace_pkg_json_folder_for_pkg_json_dep(
443 alias,
444 version_req,
445 )
446 .map_err(|e| {
447 DenoResolveErrorKind::WorkspaceResolvePkgJsonFolder(e)
448 .into_box()
449 })
450 .and_then(|pkg_folder| {
451 self
452 .node_and_npm_resolver
453 .as_ref()
454 .unwrap()
455 .node_resolver
456 .resolve_package_subpath_from_deno_module(
457 pkg_folder,
458 sub_path.as_deref(),
459 Some(referrer),
460 resolution_mode,
461 resolution_kind,
462 )
463 .map_err(|e| {
464 DenoResolveErrorKind::Node(e.into_node_resolve_error())
465 .into_box()
466 })
467 })
468 .and_then(|r| Ok(r.into_url()?)),
469 })
470 }
471 MappedResolution::PackageJsonImport { pkg_json } => self
472 .node_and_npm_resolver
473 .as_ref()
474 .unwrap()
475 .node_resolver
476 .resolve_package_import(
477 raw_specifier,
478 Some(&UrlOrPathRef::from_url(referrer)),
479 Some(pkg_json),
480 resolution_mode,
481 resolution_kind,
482 )
483 .map_err(|e| {
484 DenoResolveErrorKind::Node(
485 NodeResolveErrorKind::PackageImportsResolve(e).into_box(),
486 )
487 .into_box()
488 })
489 .and_then(|r| Ok(r.into_url()?)),
490 },
491 Err(err) => Err(err.into()),
492 };
493
494 if let Some(vendor_specifier) = &self.maybe_vendor_specifier
499 && let Ok(specifier) = &result
500 && specifier.as_str().starts_with(vendor_specifier.as_str())
501 {
502 return Err(DenoResolveErrorKind::InvalidVendorFolderImport.into_box());
503 }
504
505 let Some(NodeAndNpmResolvers {
506 node_resolver,
507 npm_req_resolver,
508 ..
509 }) = &self.node_and_npm_resolver
510 else {
511 return Ok(DenoResolution {
512 url: result?,
513 maybe_diagnostic,
514 found_package_json_dep,
515 });
516 };
517
518 match result {
519 Ok(specifier) => {
520 if specifier.scheme() == "node" {
521 let module_name = specifier.path();
522 return if node_resolver.is_builtin_node_module(module_name) {
523 Ok(DenoResolution {
524 url: specifier,
525 maybe_diagnostic,
526 found_package_json_dep,
527 })
528 } else {
529 Err(
530 NodeResolveErrorKind::UnknownBuiltInNodeModule(
531 UnknownBuiltInNodeModuleError {
532 module_name: module_name.to_string(),
533 },
534 )
535 .into_box()
536 .into(),
537 )
538 };
539 }
540
541 if let Ok(npm_req_ref) =
542 NpmPackageReqReference::from_specifier(&specifier)
543 {
544 if let Some(pkg_folder) = self
546 .workspace_resolver
547 .resolve_workspace_pkg_json_folder_for_npm_specifier(
548 npm_req_ref.req(),
549 )
550 {
551 return node_resolver
552 .resolve_package_subpath_from_deno_module(
553 pkg_folder,
554 npm_req_ref.sub_path(),
555 Some(referrer),
556 resolution_mode,
557 resolution_kind,
558 )
559 .map_err(|err| {
560 DenoResolveErrorKind::ResolveNpmReqRef(ResolveNpmReqRefError {
561 npm_req_ref: npm_req_ref.clone(),
562 err: err.into(),
563 })
564 .into_box()
565 })
566 .and_then(|url_or_path| {
567 Ok(DenoResolution {
568 url: url_or_path.into_url()?,
569 maybe_diagnostic,
570 found_package_json_dep,
571 })
572 });
573 }
574
575 if self.is_byonm {
576 return npm_req_resolver
577 .resolve_req_reference(
578 &npm_req_ref,
579 referrer,
580 resolution_mode,
581 resolution_kind,
582 )
583 .map_err(|err| {
584 DenoResolveErrorKind::ResolveNpmReqRef(err).into_box()
585 })
586 .and_then(|url_or_path| {
587 Ok(DenoResolution {
588 url: url_or_path.into_url()?,
589 maybe_diagnostic,
590 found_package_json_dep,
591 })
592 });
593 }
594 }
595
596 Ok(DenoResolution {
597 url: node_resolver
598 .handle_if_in_node_modules(&specifier)
599 .unwrap_or(specifier),
600 maybe_diagnostic,
601 found_package_json_dep,
602 })
603 }
604 Err(err) => {
605 if self.is_byonm && referrer.scheme() == "file" {
607 let maybe_resolution = npm_req_resolver
608 .resolve_if_for_npm_pkg(
609 raw_specifier,
610 referrer,
611 resolution_mode,
612 resolution_kind,
613 )
614 .map_err(|e| match e.into_kind() {
615 ResolveIfForNpmPackageErrorKind::NodeResolve(e) => {
616 DenoResolveErrorKind::Node(e).into_box()
617 }
618 ResolveIfForNpmPackageErrorKind::NodeModulesOutOfDate(e) => {
619 e.into()
620 }
621 })?;
622 if let Some(res) = maybe_resolution {
623 match res {
624 NodeResolution::Module(ref _url) => {
625 return Ok(DenoResolution {
626 url: res.into_url()?,
627 maybe_diagnostic,
628 found_package_json_dep,
629 });
630 }
631 NodeResolution::BuiltIn(ref _module) => {
632 if self.bare_node_builtins {
633 return Ok(DenoResolution {
634 url: res.into_url()?,
635 maybe_diagnostic,
636 found_package_json_dep,
637 });
638 }
639 }
640 }
641 }
642 } else if self.bare_node_builtins
643 && matches!(err.as_kind(), DenoResolveErrorKind::MappedResolution(err) if err.is_unmapped_bare_specifier())
644 && node_resolver.is_builtin_node_module(raw_specifier)
645 {
646 return Ok(DenoResolution {
647 url: Url::parse(&format!("node:{}", raw_specifier)).unwrap(),
648 maybe_diagnostic,
649 found_package_json_dep,
650 });
651 }
652
653 Err(err)
654 }
655 }
656 }
657
658 #[cfg(feature = "graph")]
659 pub(crate) fn resolve_non_workspace_npm_req_ref_to_file(
660 &self,
661 npm_req_ref: &NpmPackageReqReference,
662 referrer: &Url,
663 resolution_mode: ResolutionMode,
664 resolution_kind: NodeResolutionKind,
665 ) -> Result<node_resolver::UrlOrPath, npm::ResolveNpmReqRefError> {
666 let Some(NodeAndNpmResolvers {
667 npm_req_resolver, ..
668 }) = &self.node_and_npm_resolver
669 else {
670 return Err(npm::ResolveNpmReqRefError {
671 npm_req_ref: npm_req_ref.clone(),
672 err: npm::ResolveReqWithSubPathErrorKind::NoNpm(npm::NoNpmError)
673 .into_box(),
674 });
675 };
676 npm_req_resolver.resolve_req_reference(
677 npm_req_ref,
678 referrer,
679 resolution_mode,
680 resolution_kind,
681 )
682 }
683}