diff --git a/lib/lib.rs b/lib/lib.rs index b04a1488b..b5bba5d87 100644 --- a/lib/lib.rs +++ b/lib/lib.rs @@ -266,6 +266,7 @@ pub async fn js_create_graph( file_system: &NullFileSystem, jsr_url_provider: Default::default(), npm_resolver: None, + passthrough_jsr_specifiers: false, module_analyzer: Default::default(), imports, reporter: None, diff --git a/src/graph.rs b/src/graph.rs index 8457e7c01..8f37b7317 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1017,6 +1017,10 @@ pub struct BuildOptions<'a> { pub executor: &'a dyn Executor, pub file_system: &'a dyn FileSystem, pub jsr_url_provider: &'a dyn JsrUrlProvider, + /// Whether to pass through JSR specifiers to the resolver instead of + /// resolving them. This is useful in cases where you want to mark JSR + /// specifiers as external. + pub passthrough_jsr_specifiers: bool, pub module_analyzer: &'a dyn ModuleAnalyzer, pub npm_resolver: Option<&'a dyn NpmResolver>, pub reporter: Option<&'a dyn Reporter>, @@ -1447,6 +1451,7 @@ impl ModuleGraph { options.is_dynamic, options.file_system, options.jsr_url_provider, + options.passthrough_jsr_specifiers, options.resolver, options.npm_resolver, loader, @@ -2885,6 +2890,7 @@ struct Builder<'a, 'graph> { in_dynamic_branch: bool, file_system: &'a dyn FileSystem, jsr_url_provider: &'a dyn JsrUrlProvider, + passthrough_jsr_specifiers: bool, loader: &'a mut dyn Loader, resolver: Option<&'a dyn Resolver>, npm_resolver: Option<&'a dyn NpmResolver>, @@ -2907,6 +2913,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { is_dynamic: bool, file_system: &'a dyn FileSystem, jsr_url_provider: &'a dyn JsrUrlProvider, + passthrough_jsr_specifiers: bool, resolver: Option<&'a dyn Resolver>, npm_resolver: Option<&'a dyn NpmResolver>, loader: &'a mut dyn Loader, @@ -2923,6 +2930,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { in_dynamic_branch: is_dynamic, file_system, jsr_url_provider, + passthrough_jsr_specifiers, loader, resolver, npm_resolver, @@ -3624,7 +3632,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { } let maybe_range = maybe_range.map(ToOwned::to_owned); - if specifier.scheme() == "jsr" { + if !self.passthrough_jsr_specifiers && specifier.scheme() == "jsr" { self.load_jsr_specifier(specifier.clone(), maybe_range, is_dynamic); return; } else if let Some(npm_resolver) = self.npm_resolver { diff --git a/src/lib.rs b/src/lib.rs index 4c789932f..624fe2d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4067,4 +4067,54 @@ export function a(a: A): B { ModuleGraphError::TypesResolutionError(_) )); } + + #[tokio::test] + async fn test_passthrough_jsr_specifiers() { + let mut loader = setup( + vec![ + ( + "file:///a/test01.ts", + Source::Module { + specifier: "file:///a/test01.ts", + maybe_headers: None, + content: r#"import "jsr:@foo/bar@1";"#, + }, + ), + ("jsr:@foo/bar@1", Source::External("jsr:@foo/bar@1")), + ], + vec![], + ); + let root_specifier = + ModuleSpecifier::parse("file:///a/test01.ts").expect("bad url"); + let mut graph = ModuleGraph::new(GraphKind::All); + graph + .build( + vec![root_specifier.clone()], + &mut loader, + BuildOptions { + passthrough_jsr_specifiers: true, + ..Default::default() + }, + ) + .await; + assert_eq!(graph.module_slots.len(), 2); + assert_eq!(graph.roots, vec![root_specifier.clone()]); + assert!(graph.contains(&root_specifier)); + let module = graph + .module_slots + .get(&root_specifier) + .unwrap() + .module() + .unwrap() + .js() + .unwrap(); + assert_eq!(module.dependencies.len(), 1); + let maybe_dependency = module.dependencies.get("jsr:@foo/bar@1"); + assert!(maybe_dependency.is_some()); + let dependency_specifier = + ModuleSpecifier::parse("jsr:@foo/bar@1").unwrap(); + let maybe_dep_module_slot = graph.get(&dependency_specifier); + let dep_module_slot = maybe_dep_module_slot.unwrap(); + assert!(dep_module_slot.external().is_some()); + } }