diff --git a/Cargo.lock b/Cargo.lock index c1ad8fcbf6..8f180776c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" [[package]] name = "arrayvec" @@ -720,7 +720,7 @@ dependencies = [ "clap_complete", "clap_complete_fig", "data-url", - "deno_ast", + "deno_ast 0.10.0", "deno_bench_util", "deno_broadcast_channel", "deno_console", @@ -790,6 +790,23 @@ name = "deno_ast" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e81ceec755f9e4e270e8d7ef4ade1921eddc1717dea092c726b088f9c074721b" +dependencies = [ + "anyhow", + "base64 0.13.0", + "data-url", + "serde", + "swc_atoms", + "swc_common", + "swc_ecmascript", + "text_lines", + "url", +] + +[[package]] +name = "deno_ast" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2780a628fd2fd52c81ef3f9d2bda0609a662b9e4f85623c29051c277363f65" dependencies = [ "anyhow", "base64 0.13.0", @@ -835,7 +852,7 @@ name = "deno_core" version = "0.117.0" dependencies = [ "anyhow", - "deno_ast", + "deno_ast 0.9.0", "futures", "indexmap", "libc", @@ -882,12 +899,12 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.26.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82f45d6513b789c04adf0915ca3c3c696f5f5f9379a82834ad4f783fbaa8b3a" +checksum = "a37040708cc0c8f1f826073774ea4406bee735d8cb6aacd0b9891e27be7c13bd" dependencies = [ "cfg-if 1.0.0", - "deno_ast", + "deno_ast 0.10.0", "deno_graph", "futures", "lazy_static", @@ -928,14 +945,14 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.18.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b2a21feedd2fde8846a8b94855498ff7e1c59bdf972a06a6faf228999c1f7fd" +checksum = "86eccec721ae9dceae2e6cbd83b63de3ef810e7c39357c9a867c98b9633d4775" dependencies = [ "anyhow", "cfg-if 1.0.0", "data-url", - "deno_ast", + "deno_ast 0.10.0", "futures", "lazy_static", "parking_lot", @@ -964,12 +981,12 @@ dependencies = [ [[package]] name = "deno_lint" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf1715d924e6fdf45847868b31742ed305fb6270747c6046dc3c19f67c0d2f5" +checksum = "c838bbcaffd4143940068bea8eb5faa6de1ed8c3d3d0a63d8ed7319a967f47bd" dependencies = [ "anyhow", - "deno_ast", + "deno_ast 0.10.0", "derive_more", "if_chain", "log", @@ -1204,9 +1221,9 @@ dependencies = [ [[package]] name = "dprint-core" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8113ac9998173de8fa4fcf6fc7fe80c15c93df290828afa060dbdbb84337afbf" +checksum = "50bcbca32c5b922a8b4ededae420ca17aa7c1ac03131bebe7f78d682945e21a1" dependencies = [ "anyhow", "bumpalo", @@ -1241,12 +1258,12 @@ dependencies = [ [[package]] name = "dprint-plugin-typescript" -version = "0.62.0" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e9b87d22638bc08075c827ae568e02b07fbf262142db90895383bac3f5a67e" +checksum = "312bd921ea751a303703123e2e26e28456abd6ae9ff42dce478e16f613154874" dependencies = [ "anyhow", - "deno_ast", + "deno_ast 0.10.0", "dprint-core", "parking_lot_core", "rustc-hash", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b9dcf38fed..5b4095d55e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -45,11 +45,11 @@ winapi = "=0.3.9" winres = "=0.1.11" [dependencies] -deno_ast = { version = "0.9.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "transpiling", "typescript", "view", "visit"] } +deno_ast = { version = "0.10.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "transpiling", "typescript", "view", "visit"] } deno_core = { version = "0.117.0", path = "../core" } -deno_doc = "0.26.0" -deno_graph = "0.18.0" -deno_lint = { version = "0.22.0", features = ["docs"] } +deno_doc = "0.28.0" +deno_graph = "0.21.1" +deno_lint = { version = "0.23.0", features = ["docs"] } deno_runtime = { version = "0.43.0", path = "../runtime" } atty = "=0.2.14" @@ -63,7 +63,7 @@ data-url = "=0.1.1" dissimilar = "=1.0.2" dprint-plugin-json = "=0.14.0" dprint-plugin-markdown = "=0.12.1" -dprint-plugin-typescript = "=0.62.0" +dprint-plugin-typescript = "=0.62.1" encoding_rs = "=0.8.29" env_logger = "=0.8.4" fancy-regex = "=0.7.1" diff --git a/cli/compat/esm_resolver.rs b/cli/compat/esm_resolver.rs index 77dbc3e057..300b76f31a 100644 --- a/cli/compat/esm_resolver.rs +++ b/cli/compat/esm_resolver.rs @@ -9,6 +9,7 @@ use deno_core::serde_json::Map; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; +use deno_graph::source::ResolveResponse; use deno_graph::source::Resolver; use regex::Regex; use std::path::PathBuf; @@ -31,24 +32,28 @@ impl Resolver for NodeEsmResolver { &self, specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { + ) -> ResolveResponse { // First try to resolve using import map, ignoring any errors if !specifier.starts_with("node:") { if let Some(import_map_resolver) = &self.maybe_import_map_resolver { - if let Ok(specifier) = import_map_resolver.resolve(specifier, referrer) - { - return Ok(specifier); + let response = import_map_resolver.resolve(specifier, referrer); + if !matches!(response, ResolveResponse::Err(_)) { + return response; } } } + let current_dir = match std::env::current_dir() { + Ok(path) => path, + Err(err) => return ResolveResponse::Err(err.into()), + }; let node_resolution = - node_resolve(specifier, referrer.as_str(), &std::env::current_dir()?); + node_resolve(specifier, referrer.as_str(), ¤t_dir); match node_resolution { - Ok(specifier) => { + Ok(resolve_response) => { // If node resolution succeeded, return the specifier - Ok(specifier) + resolve_response } Err(err) => { // If node resolution failed, check if it's because of unsupported @@ -57,11 +62,13 @@ impl Resolver for NodeEsmResolver { .to_string() .starts_with("[ERR_UNSUPPORTED_ESM_URL_SCHEME]") { - return deno_core::resolve_import(specifier, referrer.as_str()) - .map_err(|err| err.into()); + return match deno_core::resolve_import(specifier, referrer.as_str()) { + Ok(specifier) => ResolveResponse::Esm(specifier), + Err(err) => ResolveResponse::Err(err.into()), + }; } - Err(err) + ResolveResponse::Err(err) } } } @@ -75,16 +82,16 @@ fn node_resolve( specifier: &str, referrer: &str, cwd: &std::path::Path, -) -> Result { +) -> Result { // TODO(bartlomieju): skipped "policy" part as we don't plan to support it if let Some(resolved) = crate::compat::try_resolve_builtin_module(specifier) { - return Ok(resolved); + return Ok(ResolveResponse::Esm(resolved)); } if let Ok(url) = Url::parse(specifier) { if url.scheme() == "data" { - return Ok(url); + return Ok(ResolveResponse::Specifier(url)); } let protocol = url.scheme(); @@ -95,7 +102,7 @@ fn node_resolve( if let Some(resolved) = crate::compat::try_resolve_builtin_module(&specifier) { - return Ok(resolved); + return Ok(ResolveResponse::Esm(resolved)); } else { return Err(generic_error(format!("Unknown module {}", specifier))); } @@ -107,7 +114,8 @@ fn node_resolve( if referrer.starts_with("data:") { let referrer_url = Url::parse(referrer)?; - return referrer_url.join(specifier).map_err(AnyError::from); + let url = referrer_url.join(specifier).map_err(AnyError::from)?; + return Ok(ResolveResponse::Specifier(url)); } } @@ -121,9 +129,23 @@ fn node_resolve( let conditions = DEFAULT_CONDITIONS; let url = module_resolve(specifier, &parent_url, conditions)?; + let resolve_response = if url.as_str().starts_with("http") { + ResolveResponse::Esm(url) + } else if url.as_str().ends_with(".js") { + let package_config = get_package_scope_config(&url)?; + if package_config.typ == "module" { + ResolveResponse::Esm(url) + } else { + ResolveResponse::CommonJs(url) + } + } else if url.as_str().ends_with(".cjs") { + ResolveResponse::CommonJs(url) + } else { + ResolveResponse::Esm(url) + }; // TODO(bartlomieju): skipped checking errors for commonJS resolution and // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(url) + Ok(resolve_response) } fn to_file_path(url: &ModuleSpecifier) -> PathBuf { @@ -1146,7 +1168,8 @@ mod tests { let actual = node_resolve("foo", main.as_str(), &cwd).unwrap(); let expected = Url::from_file_path(cwd.join("node_modules/foo/index.js")).unwrap(); - assert_eq!(actual, expected); + assert!(matches!(actual, ResolveResponse::Esm(_))); + assert_eq!(actual.to_result().unwrap(), expected); let actual = node_resolve( "data:application/javascript,console.log(\"Hello%20Deno\");", @@ -1154,12 +1177,11 @@ mod tests { &cwd, ) .unwrap(); - eprintln!("actual {}", actual); - assert_eq!( - actual, + let expected = Url::parse("data:application/javascript,console.log(\"Hello%20Deno\");") - .unwrap() - ); + .unwrap(); + assert!(matches!(actual, ResolveResponse::Specifier(_))); + assert_eq!(actual.to_result().unwrap(), expected); } #[test] @@ -1169,7 +1191,8 @@ mod tests { let actual = node_resolve("foo", main.as_str(), &cwd).unwrap(); let expected = Url::from_file_path(cwd.join("node_modules/foo/index.js")).unwrap(); - assert_eq!(actual, expected); + matches!(actual, ResolveResponse::Esm(_)); + assert_eq!(actual.to_result().unwrap(), expected); } #[test] @@ -1179,13 +1202,15 @@ mod tests { let actual = node_resolve("foo", main.as_str(), &cwd).unwrap(); let foo_js = Url::from_file_path(cwd.join("node_modules/foo/foo.js")).unwrap(); - assert_eq!(actual, foo_js); + assert!(matches!(actual, ResolveResponse::Esm(_))); + assert_eq!(actual.to_result().unwrap(), foo_js); let actual = node_resolve("bar", foo_js.as_str(), &cwd).unwrap(); let bar_js = Url::from_file_path(cwd.join("node_modules/bar/bar.js")).unwrap(); - assert_eq!(actual, bar_js); + assert!(matches!(actual, ResolveResponse::Esm(_))); + assert_eq!(actual.to_result().unwrap(), bar_js); } #[test] @@ -1196,12 +1221,12 @@ mod tests { Url::parse("https://deno.land/std@0.123.0/node/http.ts").unwrap(); let actual = node_resolve("http", main.as_str(), &cwd).unwrap(); - println!("actual {}", actual); - assert_eq!(actual, expected); + assert!(matches!(actual, ResolveResponse::Esm(_))); + assert_eq!(actual.to_result().unwrap(), expected); let actual = node_resolve("node:http", main.as_str(), &cwd).unwrap(); - println!("actual {}", actual); - assert_eq!(actual, expected); + assert!(matches!(actual, ResolveResponse::Esm(_))); + assert_eq!(actual.to_result().unwrap(), expected); } #[test] @@ -1214,14 +1239,16 @@ mod tests { cwd.join("node_modules/imports_exports/import_export.js"), ) .unwrap(); - assert_eq!(actual, expected); + assert!(matches!(actual, ResolveResponse::CommonJs(_))); + assert_eq!(actual.to_result().unwrap(), expected); // check that `imports` mapping works correctly let cwd = testdir("conditions/node_modules/imports_exports"); let main = Url::from_file_path(cwd.join("import_export.js")).unwrap(); let actual = node_resolve("#dep", main.as_str(), &cwd).unwrap(); let expected = Url::from_file_path(cwd.join("import_polyfill.js")).unwrap(); - assert_eq!(actual, expected); + assert!(matches!(actual, ResolveResponse::CommonJs(_))); + assert_eq!(actual.to_result().unwrap(), expected); } #[test] diff --git a/cli/emit.rs b/cli/emit.rs index e21013b61d..2d1a431dca 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -49,6 +49,7 @@ use deno_core::ModuleSpecifier; use deno_graph::MediaType; use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; +use deno_graph::ModuleKind; use deno_graph::ResolutionError; use std::collections::HashMap; use std::collections::HashSet; @@ -269,7 +270,7 @@ pub(crate) fn get_ts_config( /// the emittable files in the roots, so they get type checked and optionally /// emitted, otherwise they would be ignored if only imported into JavaScript. fn get_tsc_roots( - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], graph_data: &GraphData, check_js: bool, ) -> Vec<(ModuleSpecifier, MediaType)> { @@ -292,7 +293,7 @@ fn get_tsc_roots( } else { roots .iter() - .filter_map(|specifier| match graph_data.get(specifier) { + .filter_map(|(specifier, _)| match graph_data.get(specifier) { Some(ModuleEntry::Module { media_type, .. }) => { Some((specifier.clone(), *media_type)) } @@ -313,8 +314,15 @@ fn get_version(source_bytes: &[u8], config_bytes: &[u8]) -> String { ]) } -/// Determine if a given media type is emittable or not. -pub(crate) fn is_emittable(media_type: &MediaType, include_js: bool) -> bool { +/// Determine if a given module kind and media type is emittable or not. +pub(crate) fn is_emittable( + kind: &ModuleKind, + media_type: &MediaType, + include_js: bool, +) -> bool { + if matches!(kind, ModuleKind::Synthetic) { + return false; + } match &media_type { MediaType::TypeScript | MediaType::Mts @@ -366,7 +374,7 @@ pub(crate) struct CheckEmitResult { /// It is expected that it is determined if a check and/or emit is validated /// before the function is called. pub(crate) fn check_and_maybe_emit( - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], graph_data: Arc>, cache: &mut dyn Cacher, options: CheckOptions, @@ -387,7 +395,7 @@ pub(crate) fn check_and_maybe_emit( } let root_names = get_tsc_roots(roots, &segment_graph_data, check_js); if options.log_checks { - for root in roots { + for (root, _) in roots { let root_str = root.to_string(); // `$deno` specifiers are internal, don't print them. if !root_str.contains("$deno") { @@ -401,7 +409,7 @@ pub(crate) fn check_and_maybe_emit( let maybe_tsbuildinfo = if options.reload { None } else { - cache.get(CacheType::TypeScriptBuildInfo, &roots[0]) + cache.get(CacheType::TypeScriptBuildInfo, &roots[0].0) }; // to make tsc build info work, we need to consistently hash modules, so that // tsc can better determine if an emit is still valid or not, so we provide @@ -442,7 +450,7 @@ pub(crate) fn check_and_maybe_emit( if let Some(info) = &response.maybe_tsbuildinfo { // while we retrieve the build info for just the first module, it can be // used for all the roots in the graph, so we will cache it for all roots - for root in roots { + for (root, _) in roots { cache.set(CacheType::TypeScriptBuildInfo, root, info.clone())?; } } @@ -547,8 +555,8 @@ impl swc::bundler::Load for BundleLoader<'_> { if let Some(m) = self.graph.get(specifier) { let (fm, module) = transpile_module( specifier, - m.maybe_source().unwrap_or(""), - *m.media_type(), + m.maybe_source.as_ref().map(|s| s.as_str()).unwrap_or(""), + m.media_type, self.emit_options, self.cm.clone(), )?; @@ -716,7 +724,7 @@ pub(crate) fn bundle( let mut entries = HashMap::new(); entries.insert( "bundle".to_string(), - swc::common::FileName::Url(graph.roots[0].clone()), + swc::common::FileName::Url(graph.roots[0].0.clone()), ); let output = bundler .bundle(entries) @@ -795,21 +803,32 @@ pub(crate) fn emit( let mut file_count = 0_u32; for module in graph.modules() { file_count += 1; - if !is_emittable(&module.media_type, include_js) { + if !is_emittable(&module.kind, &module.media_type, include_js) { continue; } let needs_reload = options.reload && !options.reload_exclusions.contains(&module.specifier); - let version = get_version(module.source.as_bytes(), &config_bytes); - let is_valid = cache - .get(CacheType::Version, &module.specifier) - .map_or(false, |v| { - v == get_version(module.source.as_bytes(), &config_bytes) - }); + let version = get_version( + module.maybe_source.as_ref().map(|s| s.as_bytes()).unwrap(), + &config_bytes, + ); + let is_valid = + cache + .get(CacheType::Version, &module.specifier) + .map_or(false, |v| { + v == get_version( + module.maybe_source.as_ref().map(|s| s.as_bytes()).unwrap(), + &config_bytes, + ) + }); if is_valid && !needs_reload { continue; } - let transpiled_source = module.parsed_source.transpile(&emit_options)?; + let transpiled_source = module + .maybe_parsed_source + .as_ref() + .map(|ps| ps.transpile(&emit_options)) + .unwrap()?; emit_count += 1; cache.set(CacheType::Emit, &module.specifier, transpiled_source.text)?; if let Some(map) = transpiled_source.source_map { @@ -897,8 +916,8 @@ impl fmt::Display for GraphError { ModuleGraphError::ResolutionError(err) => { if matches!( err, - ResolutionError::InvalidDowngrade(_, _) - | ResolutionError::InvalidLocalImport(_, _) + ResolutionError::InvalidDowngrade { .. } + | ResolutionError::InvalidLocalImport { .. } ) { write!(f, "{}", err.to_string_with_range()) } else { @@ -918,7 +937,7 @@ pub(crate) fn to_file_map( ) -> HashMap { let mut files = HashMap::new(); for (_, result) in graph.specifiers().into_iter() { - if let Ok((specifier, media_type)) = result { + if let Ok((specifier, _, media_type)) = result { if let Some(emit) = cache.get(CacheType::Emit, &specifier) { files.insert(format!("{}.js", specifier), emit); if let Some(map) = cache.get(CacheType::SourceMap, &specifier) { @@ -935,7 +954,11 @@ pub(crate) fn to_file_map( if let Some(module) = graph.get(&specifier) { files.insert( specifier.to_string(), - module.maybe_source().unwrap_or("").to_string(), + module + .maybe_source + .as_ref() + .map(|s| s.to_string()) + .unwrap_or_else(|| "".to_string()), ); } } @@ -1027,16 +1050,29 @@ mod tests { #[test] fn test_is_emittable() { - assert!(is_emittable(&MediaType::TypeScript, false)); - assert!(!is_emittable(&MediaType::Dts, false)); - assert!(!is_emittable(&MediaType::Dcts, false)); - assert!(!is_emittable(&MediaType::Dmts, false)); - assert!(is_emittable(&MediaType::Tsx, false)); - assert!(!is_emittable(&MediaType::JavaScript, false)); - assert!(!is_emittable(&MediaType::Cjs, false)); - assert!(!is_emittable(&MediaType::Mjs, false)); - assert!(is_emittable(&MediaType::JavaScript, true)); - assert!(is_emittable(&MediaType::Jsx, false)); - assert!(!is_emittable(&MediaType::Json, false)); + assert!(is_emittable( + &ModuleKind::Esm, + &MediaType::TypeScript, + false + )); + assert!(!is_emittable( + &ModuleKind::Synthetic, + &MediaType::TypeScript, + false + )); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dts, false)); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dcts, false)); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dmts, false)); + assert!(is_emittable(&ModuleKind::Esm, &MediaType::Tsx, false)); + assert!(!is_emittable( + &ModuleKind::Esm, + &MediaType::JavaScript, + false + )); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Cjs, false)); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Mjs, false)); + assert!(is_emittable(&ModuleKind::Esm, &MediaType::JavaScript, true)); + assert!(is_emittable(&ModuleKind::Esm, &MediaType::Jsx, false)); + assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Json, false)); } } diff --git a/cli/errors.rs b/cli/errors.rs index 1cef35399e..1ae6559fec 100644 --- a/cli/errors.rs +++ b/cli/errors.rs @@ -48,8 +48,8 @@ pub(crate) fn get_module_graph_error_class( fn get_resolution_error_class(err: &ResolutionError) -> &'static str { match err { - ResolutionError::ResolverError(err, _, _) => { - get_error_class_name(err.as_ref()) + ResolutionError::ResolverError { error, .. } => { + get_error_class_name(error.as_ref()) } _ => "TypeError", } diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 92c253e31f..bd4e01bdc3 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -697,7 +697,11 @@ impl FileFetcher { } pub fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option { - if specifier.scheme() == "file" { + // TODO(@kitsonk) fix when deno_graph does not query cache for synthetic + // modules + if specifier.scheme() == "flags" { + None + } else if specifier.scheme() == "file" { specifier.to_file_path().ok() } else { self.http_cache.get_cache_filename(specifier) diff --git a/cli/graph_util.rs b/cli/graph_util.rs index efce57075a..f0a38b7a10 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -8,17 +8,24 @@ use deno_core::error::AnyError; use deno_core::ModuleSpecifier; use deno_graph::Dependency; use deno_graph::MediaType; -use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; +use deno_graph::ModuleKind; use deno_graph::Range; -use deno_graph::ResolutionError; +use deno_graph::Resolved; use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; use std::sync::Arc; +pub(crate) fn contains_specifier( + v: &[(ModuleSpecifier, ModuleKind)], + specifier: &ModuleSpecifier, +) -> bool { + v.iter().any(|(s, _)| s == specifier) +} + #[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] pub(crate) enum ModuleEntry { @@ -29,11 +36,10 @@ pub(crate) enum ModuleEntry { /// A set of type libs that the module has passed a type check with this /// session. This would consist of window, worker or both. checked_libs: HashSet, - maybe_types: Option>, + maybe_types: Option, }, Configuration { - dependencies: - BTreeMap>, + dependencies: BTreeMap, }, Error(ModuleGraphError), Redirect(ModuleSpecifier), @@ -62,64 +68,62 @@ impl GraphData { continue; } match result { - Ok((_, media_type)) => { + Ok((_, _, media_type)) => { let module = graph.get(&specifier).unwrap(); - let (code, dependencies, maybe_types) = match module { - Module::Es(es_module) => ( - es_module.source.clone(), - es_module.dependencies.clone(), - es_module - .maybe_types_dependency - .as_ref() - .and_then(|(_, r)| r.clone()), - ), - Module::Synthetic(synthetic_module) => match &synthetic_module - .maybe_source - { - // Synthetic modules with a source are actually JSON modules. - Some(source) => (source.clone(), Default::default(), None), - // Synthetic modules without a source are config roots. - None => { - let mut dependencies = BTreeMap::new(); - for (specifier, resolved) in &synthetic_module.dependencies { - if let Some(dep_result) = resolved { - dependencies.insert(specifier.clone(), dep_result.clone()); - if let Ok((specifier, referrer_range)) = dep_result { - let entry = self.referrer_map.entry(specifier.clone()); - entry.or_insert_with(|| referrer_range.clone()); - } - } + if module.kind == ModuleKind::Synthetic { + let mut dependencies = BTreeMap::new(); + for (specifier, dependency) in &module.dependencies { + if !matches!(dependency.maybe_type, Resolved::None) { + dependencies + .insert(specifier.clone(), dependency.maybe_type.clone()); + if let Resolved::Ok { + specifier, range, .. + } = &dependency.maybe_type + { + let entry = self.referrer_map.entry(specifier.clone()); + entry.or_insert_with(|| range.clone()); } - self.modules.insert( - synthetic_module.specifier.clone(), - ModuleEntry::Configuration { dependencies }, - ); - self - .configurations - .insert(synthetic_module.specifier.clone()); - continue; } - }, + } + self.modules.insert( + module.specifier.clone(), + ModuleEntry::Configuration { dependencies }, + ); + self.configurations.insert(module.specifier.clone()); + } + let code = match &module.maybe_source { + Some(source) => source.clone(), + None => continue, }; - if let Some(Ok((specifier, referrer_range))) = &maybe_types { + let maybe_types = module + .maybe_types_dependency + .as_ref() + .map(|(_, r)| r.clone()); + if let Some(Resolved::Ok { + specifier, range, .. + }) = &maybe_types + { let specifier = graph.redirects.get(specifier).unwrap_or(specifier); let entry = self.referrer_map.entry(specifier.clone()); - entry.or_insert_with(|| referrer_range.clone()); + entry.or_insert_with(|| range.clone()); } - for dep in dependencies.values() { + for dep in module.dependencies.values() { #[allow(clippy::manual_flatten)] for resolved in [&dep.maybe_code, &dep.maybe_type] { - if let Some(Ok((specifier, referrer_range))) = resolved { + if let Resolved::Ok { + specifier, range, .. + } = resolved + { let specifier = graph.redirects.get(specifier).unwrap_or(specifier); let entry = self.referrer_map.entry(specifier.clone()); - entry.or_insert_with(|| referrer_range.clone()); + entry.or_insert_with(|| range.clone()); } } } let module_entry = ModuleEntry::Module { code, - dependencies, + dependencies: module.dependencies.clone(), media_type, checked_libs: Default::default(), maybe_types, @@ -142,7 +146,7 @@ impl GraphData { /// Return `None` if any modules are not known. pub(crate) fn walk<'a>( &'a self, - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], follow_dynamic: bool, follow_type_only: bool, check_js: bool, @@ -150,7 +154,7 @@ impl GraphData { let mut result = HashMap::<&'a ModuleSpecifier, &'a ModuleEntry>::new(); let mut seen = HashSet::<&ModuleSpecifier>::new(); let mut visiting = VecDeque::<&ModuleSpecifier>::new(); - for root in roots { + for (root, _) in roots { seen.insert(root); visiting.push_back(root); } @@ -181,10 +185,10 @@ impl GraphData { )) && follow_type_only; if check_types { - if let Some(Ok((types, _))) = maybe_types { - if !seen.contains(types) { - seen.insert(types); - visiting.push_front(types); + if let Some(Resolved::Ok { specifier, .. }) = maybe_types { + if !seen.contains(specifier) { + seen.insert(specifier); + visiting.push_front(specifier); } } } @@ -196,10 +200,10 @@ impl GraphData { } #[allow(clippy::manual_flatten)] for resolved in resolutions { - if let Some(Ok((dep_specifier, _))) = resolved { - if !seen.contains(dep_specifier) { - seen.insert(dep_specifier); - visiting.push_front(dep_specifier); + if let Resolved::Ok { specifier, .. } = resolved { + if !seen.contains(specifier) { + seen.insert(specifier); + visiting.push_front(specifier); } } } @@ -207,10 +211,12 @@ impl GraphData { } } ModuleEntry::Configuration { dependencies } => { - for (dep_specifier, _) in dependencies.values().flatten() { - if !seen.contains(dep_specifier) { - seen.insert(dep_specifier); - visiting.push_front(dep_specifier); + for resolved in dependencies.values() { + if let Resolved::Ok { specifier, .. } = resolved { + if !seen.contains(specifier) { + seen.insert(specifier); + visiting.push_front(specifier); + } } } } @@ -230,7 +236,7 @@ impl GraphData { /// `roots`. Returns `None` if any roots are not known. pub(crate) fn graph_segment( &self, - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], ) -> Option { let mut modules = HashMap::new(); let mut referrer_map = HashMap::new(); @@ -257,7 +263,7 @@ impl GraphData { /// not known. pub(crate) fn check( &self, - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], follow_type_only: bool, check_js: bool, ) -> Option> { @@ -283,7 +289,7 @@ impl GraphData { )) && follow_type_only; if check_types { - if let Some(Err(error)) = maybe_types { + if let Some(Resolved::Err(error)) = maybe_types { let range = error.range(); if !range.specifier.as_str().contains("$deno") { return Some(Err(custom_error( @@ -302,7 +308,7 @@ impl GraphData { } #[allow(clippy::manual_flatten)] for resolved in resolutions { - if let Some(Err(error)) = resolved { + if let Resolved::Err(error) = resolved { let range = error.range(); if !range.specifier.as_str().contains("$deno") { return Some(Err(custom_error( @@ -318,7 +324,7 @@ impl GraphData { } ModuleEntry::Configuration { dependencies } => { for resolved_result in dependencies.values() { - if let Err(error) = resolved_result { + if let Resolved::Err(error) = resolved_result { let range = error.range(); if !range.specifier.as_str().contains("$deno") { return Some(Err(custom_error( @@ -331,7 +337,7 @@ impl GraphData { } } ModuleEntry::Error(error) => { - if !roots.contains(specifier) { + if !contains_specifier(roots, specifier) { if let Some(range) = self.referrer_map.get(specifier) { if !range.specifier.as_str().contains("$deno") { let message = error.to_string(); @@ -354,7 +360,7 @@ impl GraphData { /// Assumes that all of those modules are known. pub(crate) fn set_type_checked( &mut self, - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], lib: &TypeLib, ) { let specifiers: Vec = @@ -374,10 +380,10 @@ impl GraphData { /// Check if `roots` are all marked as type checked under `lib`. pub(crate) fn is_type_checked( &self, - roots: &[ModuleSpecifier], + roots: &[(ModuleSpecifier, ModuleKind)], lib: &TypeLib, ) -> bool { - roots.iter().all(|r| { + roots.iter().all(|(r, _)| { let found = self.follow_redirect(r); match self.modules.get(&found) { Some(ModuleEntry::Module { checked_libs, .. }) => { diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index e21bcff2d4..b05dcbc384 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -21,7 +21,10 @@ use std::thread; use tokio::sync::mpsc; use tokio::sync::oneshot; -type Request = (Vec, oneshot::Sender>); +type Request = ( + Vec<(ModuleSpecifier, deno_graph::ModuleKind)>, + oneshot::Sender>, +); /// A "server" that handles requests from the language server to cache modules /// in its own thread. @@ -105,7 +108,7 @@ impl CacheServer { /// client. pub async fn cache( &self, - roots: Vec, + roots: Vec<(ModuleSpecifier, deno_graph::ModuleKind)>, ) -> Result<(), AnyError> { let (tx, rx) = oneshot::channel::>(); if self.0.send((roots, tx)).is_err() { diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index f1ed425442..62b89e5261 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -20,6 +20,7 @@ use deno_core::error::AnyError; use deno_core::resolve_url; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; +use deno_graph::Resolved; use deno_runtime::tokio_util::create_basic_runtime; use log::error; use lspower::lsp; @@ -547,13 +548,13 @@ fn resolution_error_as_code( use deno_graph::SpecifierError; match err { - ResolutionError::InvalidDowngrade(_, _) => { + ResolutionError::InvalidDowngrade { .. } => { lsp::NumberOrString::String("invalid-downgrade".to_string()) } - ResolutionError::InvalidLocalImport(_, _) => { + ResolutionError::InvalidLocalImport { .. } => { lsp::NumberOrString::String("invalid-local-import".to_string()) } - ResolutionError::InvalidSpecifier(err, _) => match err { + ResolutionError::InvalidSpecifier { error, .. } => match error { SpecifierError::ImportPrefixMissing(_, _) => { lsp::NumberOrString::String("import-prefix-missing".to_string()) } @@ -561,7 +562,7 @@ fn resolution_error_as_code( lsp::NumberOrString::String("invalid-url".to_string()) } }, - ResolutionError::ResolverError(_, _, _) => { + ResolutionError::ResolverError { .. } => { lsp::NumberOrString::String("resolver-error".to_string()) } } @@ -575,7 +576,9 @@ fn diagnose_dependency( maybe_assert_type: Option<&str>, ) { match resolved { - Some(Ok((specifier, range))) => { + Resolved::Ok { + specifier, range, .. + } => { if let Some(doc) = documents.get(specifier) { if let Some(message) = doc.maybe_warning() { diagnostics.push(lsp::Diagnostic { @@ -633,7 +636,7 @@ fn diagnose_dependency( }); } } - Some(Err(err)) => diagnostics.push(lsp::Diagnostic { + Resolved::Err(err) => diagnostics.push(lsp::Diagnostic { range: documents::to_lsp_range(err.range()), severity: Some(lsp::DiagnosticSeverity::ERROR), code: Some(resolution_error_as_code(err)), diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 5d583330d5..19753389d0 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -22,7 +22,9 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::url; use deno_core::ModuleSpecifier; +use deno_graph::source::ResolveResponse; use deno_graph::Module; +use deno_graph::Resolved; use lspower::lsp; use once_cell::sync::Lazy; use std::collections::BTreeMap; @@ -213,58 +215,6 @@ impl AssetOrDocument { } } -// TODO(@kitsonk) expose the synthetic module from deno_graph -#[derive(Debug)] -struct SyntheticModule { - dependencies: BTreeMap, - specifier: ModuleSpecifier, -} - -impl SyntheticModule { - pub fn new( - specifier: ModuleSpecifier, - dependencies: Vec<(String, Option)>, - maybe_resolver: Option<&dyn deno_graph::source::Resolver>, - ) -> Self { - let dependencies = dependencies - .iter() - .map(|(dep, maybe_range)| { - let range = to_deno_graph_range(&specifier, maybe_range.as_ref()); - let result = if let Some(resolver) = maybe_resolver { - resolver.resolve(dep, &specifier).map_err(|err| { - if let Some(specifier_error) = - err.downcast_ref::() - { - deno_graph::ResolutionError::InvalidSpecifier( - specifier_error.clone(), - range.clone(), - ) - } else { - deno_graph::ResolutionError::ResolverError( - Arc::new(err), - dep.to_string(), - range.clone(), - ) - } - }) - } else { - deno_core::resolve_import(dep, specifier.as_str()).map_err(|err| { - deno_graph::ResolutionError::ResolverError( - Arc::new(err.into()), - dep.to_string(), - range.clone(), - ) - }) - }; - (dep.to_string(), Some(result.map(|s| (s, range)))) - }) - .collect(); - Self { - dependencies, - specifier, - } - } -} #[derive(Debug, Clone)] struct DocumentInner { /// contains the last-known-good set of dependencies from parsing the module @@ -274,7 +224,7 @@ struct DocumentInner { maybe_language_id: Option, maybe_lsp_version: Option, maybe_module: - Option>, + Option>, maybe_navigation_tree: Option>, maybe_warning: Option, specifier: ModuleSpecifier, @@ -299,16 +249,14 @@ impl Document { // we only ever do `Document::new` on on disk resources that are supposed to // be diagnosable, unlike `Document::open`, so it is safe to unconditionally // parse the module. - let maybe_module = match deno_graph::parse_module( + let maybe_module = Some(deno_graph::parse_module( &specifier, maybe_headers, content.clone(), + Some(&deno_graph::ModuleKind::Esm), maybe_resolver, Some(&parser), - ) { - Ok(m) => m.to_maybe_es_module().map(Ok), - Err(err) => Some(Err(err)), - }; + )); let dependencies = if let Some(Ok(module)) = &maybe_module { Arc::new(module.dependencies.clone()) } else { @@ -340,16 +288,14 @@ impl Document { let maybe_headers = language_id.as_headers(); let parser = SourceParser::default(); let maybe_module = if language_id.is_diagnosable() { - match deno_graph::parse_module( + Some(deno_graph::parse_module( &specifier, maybe_headers, content.clone(), + Some(&deno_graph::ModuleKind::Esm), maybe_resolver, Some(&parser), - ) { - Ok(m) => m.to_maybe_es_module().map(Ok), - Err(err) => Some(Err(err)), - } + )) } else { None }; @@ -411,16 +357,14 @@ impl Document { .map(|li| li.as_headers()) .flatten(); let parser = SourceParser::default(); - match deno_graph::parse_module( + Some(deno_graph::parse_module( &self.0.specifier, maybe_headers, content.clone(), + Some(&deno_graph::ModuleKind::Esm), maybe_resolver, Some(&parser), - ) { - Ok(m) => m.to_maybe_es_module().map(Ok), - Err(err) => Some(Err(err)), - } + )) } else { None }; @@ -504,10 +448,19 @@ impl Document { } pub fn maybe_types_dependency(&self) -> deno_graph::Resolved { - let module_result = self.0.maybe_module.as_ref()?; - let module = module_result.as_ref().ok()?; - let (_, maybe_dep) = module.maybe_types_dependency.as_ref()?; - maybe_dep.clone() + let module_result = match self.0.maybe_module.as_ref() { + Some(module_result) => module_result, + _ => return deno_graph::Resolved::None, + }; + let module = match module_result.as_ref() { + Ok(module) => module, + Err(_) => return deno_graph::Resolved::None, + }; + if let Some((_, maybe_dep)) = module.maybe_types_dependency.as_ref() { + maybe_dep.clone() + } else { + deno_graph::Resolved::None + } } pub fn media_type(&self) -> MediaType { @@ -525,18 +478,18 @@ impl Document { fn maybe_module( &self, - ) -> Option<&Result> { + ) -> Option<&Result> { self.0.maybe_module.as_ref() } pub fn maybe_parsed_source( &self, ) -> Option> { - self.maybe_module().map(|r| { - r.as_ref() - .map(|m| m.parsed_source.clone()) - .map_err(|err| err.clone()) - }) + let module_result = self.maybe_module()?; + match module_result { + Ok(module) => Some(Ok(module.maybe_parsed_source.clone()?)), + Err(err) => Some(Err(err.clone())), + } } pub fn maybe_navigation_tree(&self) -> Option> { @@ -576,14 +529,9 @@ impl Document { } } -pub(crate) fn to_hover_text( - result: &Result< - (ModuleSpecifier, deno_graph::Range), - deno_graph::ResolutionError, - >, -) -> String { +pub(crate) fn to_hover_text(result: &Resolved) -> String { match result { - Ok((specifier, _)) => match specifier.scheme() { + Resolved::Ok { specifier, .. } => match specifier.scheme() { "data" => "_(a data url)_".to_string(), "blob" => "_(a blob url)_".to_string(), _ => format!( @@ -593,7 +541,8 @@ pub(crate) fn to_hover_text( ) .replace('@', "​@"), }, - Err(_) => "_[errored]_".to_string(), + Resolved::Err(_) => "_[errored]_".to_string(), + Resolved::None => "_[missing]_".to_string(), } } @@ -802,7 +751,7 @@ pub(crate) struct Documents { file_system_docs: Arc>, /// Any imports to the context supplied by configuration files. This is like /// the imports into the a module graph in CLI. - imports: Arc>, + imports: Arc>, /// The optional import map that should be used when resolving dependencies. maybe_import_map: Option, /// The optional JSX resolver, which is used when JSX imports are configured. @@ -913,7 +862,7 @@ impl Documents { ) -> bool { let maybe_resolver = self.get_maybe_resolver(); let maybe_specifier = if let Some(resolver) = maybe_resolver { - resolver.resolve(specifier, referrer).ok() + resolver.resolve(specifier, referrer).to_result().ok() } else { deno_core::resolve_import(specifier, referrer.as_str()).ok() }; @@ -1043,14 +992,14 @@ impl Documents { results.push(None); } } else if let Some(dep) = dependencies.get(&specifier) { - if let Some(Ok((specifier, _))) = &dep.maybe_type { + if let Resolved::Ok { specifier, .. } = &dep.maybe_type { results.push(self.resolve_dependency(specifier)); - } else if let Some(Ok((specifier, _))) = &dep.maybe_code { + } else if let Resolved::Ok { specifier, .. } = &dep.maybe_code { results.push(self.resolve_dependency(specifier)); } else { results.push(None); } - } else if let Some(Some(Ok((specifier, _)))) = + } else if let Some(Resolved::Ok { specifier, .. }) = self.resolve_imports_dependency(&specifier) { // clone here to avoid double borrow of self @@ -1121,9 +1070,7 @@ impl Documents { imports .into_iter() .map(|(referrer, dependencies)| { - let dependencies = - dependencies.into_iter().map(|s| (s, None)).collect(); - let module = SyntheticModule::new( + let module = Module::new_from_type_imports( referrer.clone(), dependencies, self.get_maybe_resolver(), @@ -1167,7 +1114,9 @@ impl Documents { .insert(specifier.clone()); } } - if let Some((_, Some(Ok((dep, _))))) = &module.maybe_types_dependency { + if let Some((_, Resolved::Ok { specifier: dep, .. })) = + &module.maybe_types_dependency + { dependents_map .entry(dep.clone()) .or_default() @@ -1198,12 +1147,10 @@ impl Documents { .map(|m| { m.maybe_types_dependency .as_ref() - .map(|(_, o)| o.as_ref().map(|r| r.as_ref().ok()).flatten()) - .flatten() + .map(|(_, resolved)| resolved.clone()) }) - .flatten() - .cloned(); - if let Some((specifier, _)) = maybe_types_dependency { + .flatten(); + if let Some(Resolved::Ok { specifier, .. }) = maybe_types_dependency { self.resolve_dependency(&specifier) } else { let media_type = doc.media_type(); @@ -1221,7 +1168,7 @@ impl Documents { for module in self.imports.values() { let maybe_dep = module.dependencies.get(specifier); if maybe_dep.is_some() { - return maybe_dep; + return maybe_dep.map(|d| &d.maybe_type); } } None diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index b21081e85e..426710b831 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -8,6 +8,7 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; +use deno_graph::Resolved; use import_map::ImportMap; use log::error; use log::info; @@ -1058,34 +1059,38 @@ impl Inner { .get_code() .map(|s| self.documents.get(s)) .flatten() - .map(|d| d.maybe_types_dependency()) - .flatten(); - let value = match (&dep.maybe_code, &dep.maybe_type, &dep_maybe_types_dependency) { - (Some(code_dep), Some(type_dep), None) => format!( + .map(|d| d.maybe_types_dependency()); + let value = match (dep.maybe_code.is_none(), dep.maybe_type.is_none(), &dep_maybe_types_dependency) { + (false, false, None) => format!( "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", - to_hover_text(code_dep), - to_hover_text(type_dep) + to_hover_text(&dep.maybe_code), + to_hover_text(&dep.maybe_type) ), - (Some(code_dep), Some(type_dep), Some(types_dep)) => format!( + (false, false, Some(types_dep)) if !types_dep.is_none() => format!( "**Resolved Dependency**\n\n**Code**: {}\n**Types**: {}\n**Import Types**: {}\n", - to_hover_text(code_dep), - to_hover_text(types_dep), - to_hover_text(type_dep) - ), - (Some(code_dep), None, None) => format!( - "**Resolved Dependency**\n\n**Code**: {}\n", - to_hover_text(code_dep) - ), - (Some(code_dep), None, Some(types_dep)) => format!( - "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", - to_hover_text(code_dep), + to_hover_text(&dep.maybe_code), + to_hover_text(&dep.maybe_type), to_hover_text(types_dep) ), - (None, Some(type_dep), _) => format!( - "**Resolved Dependency**\n\n**Types**: {}\n", - to_hover_text(type_dep) + (false, false, Some(_)) => format!( + "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", + to_hover_text(&dep.maybe_code), + to_hover_text(&dep.maybe_type) ), - (None, None, _) => unreachable!("{}", json!(params)), + (false, true, Some(types_dep)) if !types_dep.is_none() => format!( + "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", + to_hover_text(&dep.maybe_code), + to_hover_text(types_dep) + ), + (false, true, _) => format!( + "**Resolved Dependency**\n\n**Code**: {}\n", + to_hover_text(&dep.maybe_code) + ), + (true, false, _) => format!( + "**Resolved Dependency**\n\n**Types**: {}\n", + to_hover_text(&dep.maybe_type) + ), + (true, true, _) => unreachable!("{}", json!(params)), }; let value = if let Some(docs) = self.module_registries.get_hover(&dep).await { @@ -2677,10 +2682,15 @@ impl Inner { params .uris .iter() - .map(|t| self.url_map.normalize_url(&t.uri)) + .map(|t| { + ( + self.url_map.normalize_url(&t.uri), + deno_graph::ModuleKind::Esm, + ) + }) .collect() } else { - vec![referrer.clone()] + vec![(referrer.clone(), deno_graph::ModuleKind::Esm)] }; if self.maybe_cache_server.is_none() { diff --git a/cli/main.rs b/cli/main.rs index b6bc0c7ad6..0e9a57382c 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -397,7 +397,9 @@ async fn compile_command( .then(|| { let root_module = graph.as_ref().modules()[0]; match root_module.media_type { - MediaType::JavaScript => Some(Ok(root_module.source.to_string())), + MediaType::JavaScript if root_module.maybe_source.is_some() => { + Some(Ok(root_module.maybe_source.clone().unwrap().to_string())) + } _ => None, } }) @@ -467,7 +469,7 @@ async fn info_command( .map(|im| im.as_resolver()) }; let graph = deno_graph::create_graph( - vec![specifier], + vec![(specifier, deno_graph::ModuleKind::Esm)], false, None, &mut cache, @@ -547,7 +549,7 @@ async fn cache_command( for file in cache_flags.files { let specifier = resolve_url_or_path(&file)?; ps.prepare_module_load( - vec![specifier], + vec![(specifier, deno_graph::ModuleKind::Esm)], false, lib.clone(), Permissions::allow_all(), @@ -639,7 +641,7 @@ async fn create_graph_and_maybe_check( }; let graph = Arc::new( deno_graph::create_graph( - vec![root], + vec![(root, deno_graph::ModuleKind::Esm)], false, maybe_imports, &mut cache, @@ -707,7 +709,7 @@ fn bundle_module_graph( ps: &ProcState, flags: &Flags, ) -> Result<(String, Option), AnyError> { - info!("{} {}", colors::green("Bundle"), graph.roots[0]); + info!("{} {}", colors::green("Bundle"), graph.roots[0].0); let (ts_config, maybe_ignored_options) = emit::get_ts_config( emit::ConfigType::Bundle, @@ -777,7 +779,7 @@ async fn bundle_command( .filter_map(|(_, r)| { r.as_ref() .ok() - .map(|(s, _)| s.to_file_path().ok()) + .map(|(s, _, _)| s.to_file_path().ok()) .flatten() }) .collect(); @@ -992,7 +994,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result { .map(|im| im.as_resolver()) }; let graph = deno_graph::create_graph( - vec![main_module.clone()], + vec![(main_module.clone(), deno_graph::ModuleKind::Esm)], false, maybe_imports, &mut cache, @@ -1016,7 +1018,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result { .filter_map(|(_, r)| { r.as_ref() .ok() - .map(|(s, _)| s.to_file_path().ok()) + .map(|(s, _, _)| s.to_file_path().ok()) .flatten() }) .collect(); diff --git a/cli/module_loader.rs b/cli/module_loader.rs index afd47c2d42..b6eb5ca516 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -108,7 +108,7 @@ impl ModuleLoader for CliModuleLoader { async move { ps.prepare_module_load( - vec![specifier], + vec![(specifier, deno_graph::ModuleKind::Esm)], is_dynamic, lib, root_permissions, diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index 548b20eadb..3df93862d6 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -24,6 +24,7 @@ use deno_core::serde_json::Value; use deno_core::Extension; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_graph::ModuleKind; use deno_runtime::permissions::Permissions; use serde::Deserialize; use serde::Serialize; @@ -207,9 +208,9 @@ async fn op_emit( .as_ref() .map(|imr| imr.as_resolver()) }; - let roots = vec![resolve_url_or_path(&root_specifier)?]; + let roots = vec![(resolve_url_or_path(&root_specifier)?, ModuleKind::Esm)]; let maybe_imports = - to_maybe_imports(&roots[0], args.compiler_options.as_ref()); + to_maybe_imports(&roots[0].0, args.compiler_options.as_ref()); let graph = Arc::new( deno_graph::create_graph( roots, diff --git a/cli/proc_state.rs b/cli/proc_state.rs index 4264e32e3e..320e20ac4d 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -23,6 +23,7 @@ use crate::resolver::JsxResolver; use crate::source_maps::SourceMapGetter; use crate::version; +use deno_ast::MediaType; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::custom_error; @@ -41,7 +42,8 @@ use deno_graph::create_graph; use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::Loader; -use deno_graph::MediaType; +use deno_graph::ModuleKind; +use deno_graph::Resolved; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_web::BlobStore; @@ -252,7 +254,7 @@ impl ProcState { /// emits where necessary or report any module graph / type checking errors. pub(crate) async fn prepare_module_load( &self, - roots: Vec, + roots: Vec<(ModuleSpecifier, ModuleKind)>, is_dynamic: bool, lib: emit::TypeLib, root_permissions: Permissions, @@ -262,7 +264,7 @@ impl ProcState { // TODO(bartlomieju): this is very make-shift, is there an existing API // that we could include it like with "maybe_imports"? let roots = if self.flags.compat { - let mut r = vec![compat::GLOBAL_URL.clone()]; + let mut r = vec![(compat::GLOBAL_URL.clone(), ModuleKind::Esm)]; r.extend(roots); r } else { @@ -438,21 +440,21 @@ impl ProcState { let graph_data = self.graph_data.read(); let found_referrer = graph_data.follow_redirect(&referrer); let maybe_resolved = match graph_data.get(&found_referrer) { - Some(ModuleEntry::Module { dependencies, .. }) => dependencies - .get(specifier) - .and_then(|dep| dep.maybe_code.clone()), + Some(ModuleEntry::Module { dependencies, .. }) => { + dependencies.get(specifier).map(|d| &d.maybe_code) + } _ => None, }; match maybe_resolved { - Some(Ok((specifier, _))) => return Ok(specifier), - Some(Err(err)) => { + Some(Resolved::Ok { specifier, .. }) => return Ok(specifier.clone()), + Some(Resolved::Err(err)) => { return Err(custom_error( "TypeError", format!("{}\n", err.to_string_with_range()), )) } - None => {} + Some(Resolved::None) | None => {} } } @@ -472,7 +474,7 @@ impl ProcState { None }; if let Some(resolver) = &maybe_resolver { - resolver.resolve(specifier, &referrer) + resolver.resolve(specifier, &referrer).to_result() } else { deno_core::resolve_import(specifier, referrer.as_str()) .map_err(|err| err.into()) diff --git a/cli/resolver.rs b/cli/resolver.rs index f040e1e709..fde13d7276 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,8 +1,8 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; use deno_core::resolve_import; use deno_core::ModuleSpecifier; +use deno_graph::source::ResolveResponse; use deno_graph::source::Resolver; use import_map::ImportMap; use std::sync::Arc; @@ -28,11 +28,11 @@ impl Resolver for ImportMapResolver { &self, specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { - self - .0 - .resolve(specifier, referrer) - .map_err(|err| err.into()) + ) -> ResolveResponse { + match self.0.resolve(specifier, referrer) { + Ok(specifier) => ResolveResponse::Specifier(specifier), + Err(err) => ResolveResponse::Err(err.into()), + } } } @@ -67,9 +67,12 @@ impl Resolver for JsxResolver { &self, specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { + ) -> ResolveResponse { self.maybe_import_map_resolver.as_ref().map_or_else( - || resolve_import(specifier, referrer.as_str()).map_err(|err| err.into()), + || match resolve_import(specifier, referrer.as_str()) { + Ok(specifier) => ResolveResponse::Specifier(specifier), + Err(err) => ResolveResponse::Err(err.into()), + }, |r| r.resolve(specifier, referrer), ) } diff --git a/cli/tests/testdata/055_info_file_json.out b/cli/tests/testdata/055_info_file_json.out index a85bc83c4d..2dc10a0c2c 100644 --- a/cli/tests/testdata/055_info_file_json.out +++ b/cli/tests/testdata/055_info_file_json.out @@ -22,10 +22,10 @@ } } ], + "kind": "esm", "local": "[WILDCARD]005_more_imports.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/005_more_imports.ts" }, { @@ -47,18 +47,17 @@ } } ], + "kind": "esm", "local": "[WILDCARD]mod1.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/subdir/mod1.ts" }, { - "dependencies": [], + "kind": "esm", "local": "[WILDCARD]print_hello.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/subdir/print_hello.ts" }, { @@ -80,10 +79,10 @@ } } ], + "kind": "esm", "local": "[WILDCARD]mod2.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts" } ], diff --git a/cli/tests/testdata/076_info_json_deps_order.out b/cli/tests/testdata/076_info_json_deps_order.out index 69727e6e80..db890ef022 100644 --- a/cli/tests/testdata/076_info_json_deps_order.out +++ b/cli/tests/testdata/076_info_json_deps_order.out @@ -22,10 +22,10 @@ } } ], + "kind": "esm", "local": "[WILDCARD]076_info_json_deps_order.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/076_info_json_deps_order.ts" }, { @@ -63,10 +63,10 @@ } } ], + "kind": "esm", "local": "[WILDCARD]A.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/recursive_imports/A.ts" }, { @@ -104,10 +104,10 @@ } } ], + "kind": "esm", "local": "[WILDCARD]B.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/recursive_imports/B.ts" }, { @@ -145,18 +145,17 @@ } } ], + "kind": "esm", "local": "[WILDCARD]C.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/recursive_imports/C.ts" }, { - "dependencies": [], + "kind": "esm", "local": "[WILDCARD]common.ts", [WILDCARD] "mediaType": "TypeScript", - [WILDCARD] "specifier": "file://[WILDCARD]/recursive_imports/common.ts" } ], diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index e52693b163..6e17c9fca2 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -18,7 +18,9 @@ use deno_graph::create_graph; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; +use deno_graph::source::ResolveResponse; use deno_graph::source::Resolver; +use deno_graph::ModuleKind; use deno_graph::ModuleSpecifier; use deno_runtime::permissions::Permissions; use import_map::ImportMap; @@ -47,17 +49,18 @@ impl Resolver for DocResolver { &self, specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { + ) -> ResolveResponse { if let Some(import_map) = &self.import_map { - return import_map - .resolve(specifier, referrer) - .map_err(AnyError::from); + return match import_map.resolve(specifier, referrer) { + Ok(specifier) => ResolveResponse::Specifier(specifier), + Err(err) => ResolveResponse::Err(err.into()), + }; } - let module_specifier = - deno_core::resolve_import(specifier, referrer.as_str())?; - - Ok(module_specifier) + match deno_core::resolve_import(specifier, referrer.as_str()) { + Ok(specifier) => ResolveResponse::Specifier(specifier), + Err(err) => ResolveResponse::Err(err.into()), + } } } @@ -104,7 +107,7 @@ pub async fn print_docs( let source_file_specifier = ModuleSpecifier::parse("deno://lib.deno.d.ts").unwrap(); let graph = create_graph( - vec![source_file_specifier.clone()], + vec![(source_file_specifier.clone(), ModuleKind::Esm)], false, None, &mut loader, @@ -144,7 +147,7 @@ pub async fn print_docs( import_map: ps.maybe_import_map.clone(), }; let graph = create_graph( - vec![root_specifier.clone()], + vec![(root_specifier.clone(), ModuleKind::Esm)], false, None, &mut loader, diff --git a/cli/tools/test.rs b/cli/tools/test.rs index daae61d7f6..2cc1ac808b 100644 --- a/cli/tools/test.rs +++ b/cli/tools/test.rs @@ -15,6 +15,7 @@ use crate::flags::TestFlags; use crate::fs_util::collect_specifiers; use crate::fs_util::is_supported_test_ext; use crate::fs_util::is_supported_test_path; +use crate::graph_util::contains_specifier; use crate::graph_util::graph_valid; use crate::located_script_name; use crate::lockfile; @@ -34,7 +35,7 @@ use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; -use deno_graph::Module; +use deno_graph::ModuleKind; use deno_runtime::permissions::Permissions; use deno_runtime::tokio_util::run_basic; use log::Level; @@ -724,7 +725,7 @@ async fn check_specifiers( if !inline_files.is_empty() { let specifiers = inline_files .iter() - .map(|file| file.specifier.clone()) + .map(|file| (file.specifier.clone(), ModuleKind::Esm)) .collect(); for file in inline_files { @@ -746,7 +747,7 @@ async fn check_specifiers( .iter() .filter_map(|(specifier, mode)| { if *mode != TestMode::Documentation { - Some(specifier.clone()) + Some((specifier.clone(), ModuleKind::Esm)) } else { None } @@ -1113,7 +1114,7 @@ pub async fn run_tests_with_watch( } else { test_modules .iter() - .filter_map(|url| deno_core::resolve_url(url.as_str()).ok()) + .map(|url| (url.clone(), ModuleKind::Esm)) .collect() }; let maybe_imports = if let Some(result) = maybe_imports { @@ -1129,7 +1130,10 @@ pub async fn run_tests_with_watch( .map(|im| im.as_resolver()) }; let graph = deno_graph::create_graph( - test_modules.clone(), + test_modules + .iter() + .map(|s| (s.clone(), ModuleKind::Esm)) + .collect(), false, maybe_imports, cache.as_mut_loader(), @@ -1151,7 +1155,7 @@ pub async fn run_tests_with_watch( output: &mut HashSet<&'a ModuleSpecifier>, no_check: bool, ) { - if let Some(Module::Es(module)) = maybe_module { + if let Some(module) = maybe_module { for dep in module.dependencies.values() { if let Some(specifier) = &dep.get_code() { if !output.contains(specifier) { @@ -1197,7 +1201,7 @@ pub async fn run_tests_with_watch( deno_core::resolve_url_or_path(&path.to_string_lossy()).ok() }) { if modules.contains(&&path) { - modules_to_reload.push(specifier); + modules_to_reload.push((specifier, ModuleKind::Esm)); break; } } @@ -1228,7 +1232,7 @@ pub async fn run_tests_with_watch( }) }; - let operation = |modules_to_reload: Vec| { + let operation = |modules_to_reload: Vec<(ModuleSpecifier, ModuleKind)>| { let filter = test_flags.filter.clone(); let include = include.clone(); let ignore = ignore.clone(); @@ -1245,7 +1249,9 @@ pub async fn run_tests_with_watch( ) .await? .iter() - .filter(|(specifier, _)| modules_to_reload.contains(specifier)) + .filter(|(specifier, _)| { + contains_specifier(&modules_to_reload, specifier) + }) .cloned() .collect::>(); diff --git a/cli/tsc.rs b/cli/tsc.rs index 9dc457fc03..c063925aba 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -25,6 +25,7 @@ use deno_core::ModuleSpecifier; use deno_core::OpFn; use deno_core::RuntimeOptions; use deno_core::Snapshot; +use deno_graph::Resolved; use once_cell::sync::Lazy; use std::collections::HashMap; use std::path::PathBuf; @@ -513,8 +514,12 @@ fn op_resolve(state: &mut State, args: ResolveArgs) -> Result { let referrer = graph_data.follow_redirect(&referrer); let resolved_dep = match graph_data.get(&referrer) { Some(ModuleEntry::Module { dependencies, .. }) => { - dependencies.get(specifier).and_then(|d| { - d.maybe_type.as_ref().or_else(|| d.maybe_code.as_ref()) + dependencies.get(specifier).map(|d| { + if matches!(d.maybe_type, Resolved::Ok { .. }) { + &d.maybe_type + } else { + &d.maybe_code + } }) } Some(ModuleEntry::Configuration { dependencies }) => { @@ -523,7 +528,7 @@ fn op_resolve(state: &mut State, args: ResolveArgs) -> Result { _ => None, }; let maybe_result = match resolved_dep { - Some(Ok((specifier, _))) => { + Some(Resolved::Ok { specifier, .. }) => { let specifier = graph_data.follow_redirect(specifier); match graph_data.get(&specifier) { Some(ModuleEntry::Module { @@ -531,8 +536,8 @@ fn op_resolve(state: &mut State, args: ResolveArgs) -> Result { maybe_types, .. }) => match maybe_types { - Some(Ok((types, _))) => { - let types = graph_data.follow_redirect(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)) @@ -698,6 +703,7 @@ mod tests { use crate::diagnostics::DiagnosticCategory; use crate::emit::Stats; use deno_core::futures::future; + use deno_graph::ModuleKind; use std::fs; #[derive(Debug, Default)] @@ -741,7 +747,7 @@ mod tests { let fixtures = test_util::testdata_path().join("tsc2"); let mut loader = MockLoader { fixtures }; let graph = deno_graph::create_graph( - vec![specifier], + vec![(specifier, ModuleKind::Esm)], false, None, &mut loader, @@ -768,7 +774,7 @@ mod tests { let fixtures = test_util::testdata_path().join("tsc2"); let mut loader = MockLoader { fixtures }; let graph = deno_graph::create_graph( - vec![specifier.clone()], + vec![(specifier.clone(), ModuleKind::Esm)], false, None, &mut loader,