From 1cd226750065e6101c0a38694ff9f96a40b437d0 Mon Sep 17 00:00:00 2001 From: KrisChambers Date: Mon, 7 Sep 2020 09:59:47 -0400 Subject: [PATCH] feat(info): Dependency count and sizes (#6786) This commit changes "deno info" subcommand logic. - Modules are no longer loaded into V8 isolate - analysis is done using ModuleGraph. - Removed deno_core::Deps structure. - Modules are no longer type-checked and transpiled - "compiled" file is shown only if it is already available. - Added number of unique dependencies for root module. - Changed tree output: - file size is shown next to the dependency - repeated dependencies are marked with "*" - used less spaces in prefix to save terminal width --- cli/global_state.rs | 4 +- cli/info.rs | 471 ++++++++++++++++++++++ cli/main.rs | 159 +------- cli/module_graph.rs | 8 +- cli/tests/022_info_flag_script.out | 21 +- cli/tests/031_info_no_check.out | 5 +- cli/tests/049_info_flag_script_jsx.out | 21 +- cli/tests/054_info_local_imports.out | 11 +- cli/tests/055_info_file_json.out | 43 +- cli/tests/cafile_info.ts.out | 21 +- cli/tests/info_recursive_imports_test.out | 11 + cli/tests/info_recursive_imports_test.ts | 5 + cli/tests/info_type_import.out | 8 +- cli/tests/integration_tests.rs | 38 ++ cli/tests/recursive_imports/A.ts | 7 + cli/tests/recursive_imports/B.ts | 7 + cli/tests/recursive_imports/C.ts | 8 + cli/tests/recursive_imports/common.ts | 2 + cli/tsc.rs | 8 +- core/lib.rs | 1 - core/modules.rs | 156 ------- docs/tools/dependency_inspector.md | 107 +++-- 22 files changed, 692 insertions(+), 430 deletions(-) create mode 100644 cli/info.rs create mode 100644 cli/tests/info_recursive_imports_test.out create mode 100644 cli/tests/info_recursive_imports_test.ts create mode 100644 cli/tests/recursive_imports/A.ts create mode 100644 cli/tests/recursive_imports/B.ts create mode 100644 cli/tests/recursive_imports/C.ts create mode 100644 cli/tests/recursive_imports/common.ts diff --git a/cli/global_state.rs b/cli/global_state.rs index 6add7b7b4f..2dfec4a722 100644 --- a/cli/global_state.rs +++ b/cli/global_state.rs @@ -160,11 +160,11 @@ impl GlobalState { if should_compile { if self.flags.no_check { - self.ts_compiler.transpile(module_graph).await?; + self.ts_compiler.transpile(&module_graph).await?; } else { self .ts_compiler - .compile(self, &out, target_lib, permissions, module_graph, allow_js) + .compile(self, &out, target_lib, permissions, &module_graph, allow_js) .await?; } } diff --git a/cli/info.rs b/cli/info.rs new file mode 100644 index 0000000000..c876c57d5b --- /dev/null +++ b/cli/info.rs @@ -0,0 +1,471 @@ +use crate::colors; +use crate::global_state::GlobalState; +use crate::module_graph::{ModuleGraph, ModuleGraphFile, ModuleGraphLoader}; +use crate::msg; +use crate::ModuleSpecifier; +use crate::Permissions; +use deno_core::ErrBox; +use serde::Serialize; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +// TODO(bartlomieju): rename +/// Struct containing a module's dependency information. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ModuleDepInfo { + local: String, + file_type: String, + compiled: Option, + map: Option, + dep_count: usize, + deps: FileInfoDepTree, +} + +impl ModuleDepInfo { + /// Creates a new `ModuleDepInfo` struct for the module with the provided `ModuleSpecifier`. + pub async fn new( + global_state: &Arc, + module_specifier: ModuleSpecifier, + ) -> Result { + // First load module as if it was to be executed by worker + // including compilation step + let mut module_graph_loader = ModuleGraphLoader::new( + global_state.file_fetcher.clone(), + global_state.maybe_import_map.clone(), + Permissions::allow_all(), + false, + true, + ); + module_graph_loader + .add_to_graph(&module_specifier, None) + .await?; + let module_graph = module_graph_loader.get_graph(); + + let ts_compiler = &global_state.ts_compiler; + let file_fetcher = &global_state.file_fetcher; + let out = file_fetcher + .fetch_cached_source_file(&module_specifier, Permissions::allow_all()) + .expect("Source file should already be cached"); + let local_filename = out.filename.to_string_lossy().to_string(); + let compiled_filename = ts_compiler + .get_compiled_source_file(&out.url) + .ok() + .map(|file| file.filename.to_string_lossy().to_string()); + let map_filename = ts_compiler + .get_source_map_file(&module_specifier) + .ok() + .map(|file| file.filename.to_string_lossy().to_string()); + let file_type = msg::enum_name_media_type(out.media_type).to_string(); + + let deps = FileInfoDepTree::new(&module_graph, &module_specifier); + let dep_count = get_unique_dep_count(&module_graph) - 1; + + let info = Self { + local: local_filename, + file_type, + compiled: compiled_filename, + map: map_filename, + dep_count, + deps, + }; + + Ok(info) + } +} + +/// Counts the number of dependencies in the graph. +/// +/// We are counting only the dependencies that are not http redirects to other files. +fn get_unique_dep_count(graph: &ModuleGraph) -> usize { + graph.iter().fold( + 0, + |acc, e| { + if e.1.redirect.is_none() { + acc + 1 + } else { + acc + } + }, + ) +} + +impl std::fmt::Display for ModuleDepInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{} {}\n", colors::bold("local:"), self.local))?; + f.write_fmt(format_args!( + "{} {}\n", + colors::bold("type:"), + self.file_type + ))?; + if let Some(ref compiled) = self.compiled { + f.write_fmt(format_args!( + "{} {}\n", + colors::bold("compiled:"), + compiled + ))?; + } + if let Some(ref map) = self.map { + f.write_fmt(format_args!("{} {}\n", colors::bold("map:"), map))?; + } + + f.write_fmt(format_args!( + "{} {} unique {}\n", + colors::bold("deps:"), + self.dep_count, + colors::gray(&format!( + "(total {})", + human_size(self.deps.total_size.unwrap_or(0) as f64), + )) + ))?; + f.write_fmt(format_args!( + "{} {}\n", + self.deps.name, + colors::gray(&format!("({})", human_size(self.deps.size as f64))) + ))?; + + for (idx, dep) in self.deps.deps.iter().enumerate() { + print_file_dep_info(&dep, "", idx == self.deps.deps.len() - 1, f)?; + } + + Ok(()) + } +} + +/// A dependency tree of the basic module information. +/// +/// Constructed from a `ModuleGraph` and `ModuleSpecifier` that +/// acts as the root of the tree. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct FileInfoDepTree { + name: String, + size: usize, + total_size: Option, + deps: Vec, +} + +impl FileInfoDepTree { + /// Create a `FileInfoDepTree` tree from a `ModuleGraph` and the root `ModuleSpecifier`. + pub fn new( + module_graph: &ModuleGraph, + root_specifier: &ModuleSpecifier, + ) -> Self { + let mut seen = HashSet::new(); + let mut total_sizes = HashMap::new(); + + Self::visit_module( + &mut seen, + &mut total_sizes, + module_graph, + root_specifier, + ) + } + + /// Visit modules recursively. + /// + /// If currently visited module has not yet been seen it will be annotated with dependencies + /// and cumulative size of those deps. + fn visit_module( + seen: &mut HashSet, + total_sizes: &mut HashMap, + graph: &ModuleGraph, + specifier: &ModuleSpecifier, + ) -> Self { + let name = specifier.to_string(); + let never_seen = seen.insert(name.clone()); + let file = get_resolved_file(&graph, &specifier); + let size = file.size(); + let mut deps = vec![]; + let mut total_size = None; + + if never_seen { + let mut seen_deps = HashSet::new(); + deps = file + .imports + .iter() + .map(|import| &import.resolved_specifier) + .filter(|module_specifier| { + seen_deps.insert(module_specifier.as_str().to_string()) + }) + .map(|specifier| { + Self::visit_module(seen, total_sizes, graph, specifier) + }) + .collect::>(); + + total_size = if let Some(total_size) = total_sizes.get(&name) { + Some(total_size.to_owned()) + } else { + let total: usize = deps + .iter() + .map(|dep| { + if let Some(total_size) = dep.total_size { + total_size + } else { + 0 + } + }) + .sum(); + let total = size + total; + + total_sizes.insert(name.clone(), total); + + Some(total) + }; + } + + Self { + name, + size, + total_size, + deps, + } + } +} + +/// Returns a `ModuleGraphFile` associated to the provided `ModuleSpecifier`. +/// +/// If the `specifier` is associated with a file that has a populated redirect field, +/// it returns the file associated to the redirect, otherwise the file associated to `specifier`. +fn get_resolved_file<'a>( + graph: &'a ModuleGraph, + specifier: &ModuleSpecifier, +) -> &'a ModuleGraphFile { + // Note(kc): This code is dependent on how we are injecting a dummy ModuleGraphFile + // into the graph with a "redirect" property. + let result = graph.get(specifier.as_str()).unwrap(); + + if let Some(ref import) = result.redirect { + graph.get(import).unwrap() + } else { + result + } +} + +/// Prints the `FileInfoDepTree` tree to stdout. +fn print_file_dep_info( + info: &FileInfoDepTree, + prefix: &str, + is_last: bool, + formatter: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + print_dep(prefix, is_last, info, formatter)?; + + let prefix = &get_new_prefix(prefix, is_last); + let child_count = info.deps.len(); + for (idx, dep) in info.deps.iter().enumerate() { + print_file_dep_info(dep, prefix, idx == child_count - 1, formatter)?; + } + + Ok(()) +} + +/// Prints a single `FileInfoDepTree` to stdout. +fn print_dep( + prefix: &str, + is_last: bool, + info: &FileInfoDepTree, + formatter: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + let has_children = !info.deps.is_empty(); + + formatter.write_fmt(format_args!( + "{} {}{}\n", + colors::gray(&format!( + "{}{}─{}", + prefix, + get_sibling_connector(is_last), + get_child_connector(has_children), + )) + .to_string(), + info.name, + get_formatted_totals(info) + )) +} + +/// Gets the formatted totals for the provided `FileInfoDepTree`. +/// +/// If the total size is reported as 0 then an empty string is returned. +fn get_formatted_totals(info: &FileInfoDepTree) -> String { + if let Some(_total_size) = info.total_size { + colors::gray(&format!(" ({})", human_size(info.size as f64),)).to_string() + } else { + // This dependency has already been displayed somewhere else in the tree. + colors::gray(" *").to_string() + } +} + +/// Gets the sibling portion of the tree branch. +fn get_sibling_connector(is_last: bool) -> char { + if is_last { + '└' + } else { + '├' + } +} + +/// Gets the child connector for the branch. +fn get_child_connector(has_children: bool) -> char { + if has_children { + '┬' + } else { + '─' + } +} + +/// Creates a new prefix for a dependency tree item. +fn get_new_prefix(prefix: &str, is_last: bool) -> String { + let mut prefix = prefix.to_string(); + if is_last { + prefix.push(' '); + } else { + prefix.push('│'); + } + + prefix.push(' '); + prefix +} + +pub fn human_size(bytse: f64) -> String { + let negative = if bytse.is_sign_positive() { "" } else { "-" }; + let bytse = bytse.abs(); + let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + if bytse < 1_f64 { + return format!("{}{}{}", negative, bytse, "B"); + } + let delimiter = 1024_f64; + let exponent = std::cmp::min( + (bytse.ln() / delimiter.ln()).floor() as i32, + (units.len() - 1) as i32, + ); + let pretty_bytes = format!("{:.2}", bytse / delimiter.powi(exponent)) + .parse::() + .unwrap() + * 1_f64; + let unit = units[exponent as usize]; + format!("{}{}{}", negative, pretty_bytes, unit) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::module_graph::ImportDescriptor; + use crate::swc_util::Location; + use crate::MediaType; + + #[test] + fn human_size_test() { + assert_eq!(human_size(16_f64), "16B"); + assert_eq!(human_size((16 * 1024) as f64), "16KB"); + assert_eq!(human_size((16 * 1024 * 1024) as f64), "16MB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(3.0)), "16GB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(4.0)), "16TB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(5.0)), "16PB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(6.0)), "16EB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(7.0)), "16ZB"); + assert_eq!(human_size(16_f64 * 1024_f64.powf(8.0)), "16YB"); + } + + #[test] + fn get_new_prefix_adds_spaces_if_is_last() { + let prefix = get_new_prefix("", true); + + assert_eq!(prefix, " ".to_string()); + } + + #[test] + fn get_new_prefix_adds_a_vertial_bar_if_not_is_last() { + let prefix = get_new_prefix("", false); + + assert_eq!(prefix, "│ ".to_string()); + } + + fn create_mock_file( + name: &str, + imports: Vec, + redirect: Option, + ) -> (ModuleGraphFile, ModuleSpecifier) { + let spec = ModuleSpecifier::from( + url::Url::parse(&format!("http://{}", name)).unwrap(), + ); + let file = ModuleGraphFile { + filename: "name".to_string(), + imports: imports + .iter() + .map(|import| ImportDescriptor { + specifier: import.to_string(), + resolved_specifier: import.clone(), + resolved_type_directive: None, + type_directive: None, + location: Location { + col: 0, + filename: "".to_string(), + line: 0, + }, + }) + .collect(), + lib_directives: vec![], + media_type: MediaType::TypeScript, + redirect: redirect.map(|x| x.to_string()), + referenced_files: vec![], + source_code: "".to_string(), + specifier: spec.to_string(), + type_headers: vec![], + types_directives: vec![], + version_hash: "".to_string(), + url: "".to_string(), + }; + + (file, spec) + } + + #[test] + fn get_resolved_file_test() { + let (test_file_redirect, redirect) = + create_mock_file("test_redirect", vec![], None); + let (test_file, original) = + create_mock_file("test", vec![], Some(redirect.clone())); + + let mut graph = ModuleGraph::new(); + graph.insert(original.to_string(), test_file); + graph.insert(redirect.to_string(), test_file_redirect); + + let file = get_resolved_file(&graph, &original); + + assert_eq!(file.specifier, redirect.to_string()); + } + + #[test] + fn dependency_count_no_redirects() { + let (a, aspec) = create_mock_file("a", vec![], None); + let (b, bspec) = create_mock_file("b", vec![aspec.clone()], None); + let (c, cspec) = create_mock_file("c", vec![bspec.clone()], None); + + let mut graph = ModuleGraph::new(); + + graph.insert(aspec.to_string(), a); + graph.insert(bspec.to_string(), b); + graph.insert(cspec.to_string(), c); + + let count = get_unique_dep_count(&graph); + + assert_eq!(graph.len(), count); + } + + #[test] + fn dependency_count_with_redirects() { + let (a, aspec) = create_mock_file("a", vec![], None); + let (b, bspec) = create_mock_file("b", vec![], Some(aspec.clone())); + let (c, cspec) = create_mock_file("c", vec![bspec.clone()], None); + + let mut graph = ModuleGraph::new(); + + graph.insert(aspec.to_string(), a); + graph.insert(bspec.to_string(), b); + graph.insert(cspec.to_string(), c); + + let count = get_unique_dep_count(&graph); + + assert_eq!(graph.len() - 1, count); + } +} diff --git a/cli/main.rs b/cli/main.rs index 11b12f3faf..d5329eeb4b 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -40,6 +40,7 @@ mod global_timer; pub mod http_cache; mod http_util; mod import_map; +mod info; mod inspector; pub mod installer; mod js; @@ -76,12 +77,9 @@ use crate::fs as deno_fs; use crate::global_state::GlobalState; use crate::msg::MediaType; use crate::permissions::Permissions; -use crate::tsc::TargetLib; use crate::worker::MainWorker; use deno_core::v8_set_flags; -use deno_core::Deps; use deno_core::ErrBox; -use deno_core::JsRuntime; use deno_core::ModuleSpecifier; use deno_doc as doc; use deno_doc::parser::DocFileLoader; @@ -151,114 +149,6 @@ fn print_cache_info( } } -struct FileInfoOutput<'a> { - local: &'a str, - file_type: &'a str, - compiled: Option, - map: Option, - deps: Option, -} - -// TODO(bartlomieju): this function de facto repeats -// whole compilation stack. Can this be done better somehow? -async fn print_file_info( - worker: &MainWorker, - module_specifier: ModuleSpecifier, - json: bool, -) -> Result<(), ErrBox> { - let global_state = worker.state.global_state.clone(); - - let out = global_state - .file_fetcher - .fetch_source_file(&module_specifier, None, Permissions::allow_all()) - .await?; - - let mut output = FileInfoOutput { - local: out.filename.to_str().unwrap(), - file_type: msg::enum_name_media_type(out.media_type), - compiled: None, - map: None, - deps: None, - }; - - let module_specifier_ = module_specifier.clone(); - - global_state - .prepare_module_load( - module_specifier_.clone(), - None, - TargetLib::Main, - Permissions::allow_all(), - false, - global_state.maybe_import_map.clone(), - ) - .await?; - global_state - .clone() - .fetch_compiled_module(module_specifier_, None) - .await?; - - if out.media_type == msg::MediaType::TypeScript - || (out.media_type == msg::MediaType::JavaScript - && global_state.ts_compiler.compile_js) - { - let compiled_source_file = global_state - .ts_compiler - .get_compiled_source_file(&out.url) - .unwrap(); - output.compiled = - compiled_source_file.filename.to_str().map(|s| s.to_owned()); - } - - if let Ok(source_map) = global_state - .clone() - .ts_compiler - .get_source_map_file(&module_specifier) - { - output.map = source_map.filename.to_str().map(|s| s.to_owned()); - } - let es_state_rc = JsRuntime::state(&worker.isolate); - let es_state = es_state_rc.borrow(); - - if let Some(deps) = es_state.modules.deps(&module_specifier) { - output.deps = Some(deps); - } - - if json { - let output = json!({ - "local": output.local, - "fileType": output.file_type, - "compiled": output.compiled, - "map": output.map, - "deps": output.deps.map(|x| x.to_json()) - }); - write_json_to_stdout(&output) - } else { - println!("{} {}", colors::bold("local:"), output.local); - println!("{} {}", colors::bold("type:"), output.file_type); - if let Some(compiled) = output.compiled { - println!("{} {}", colors::bold("compiled:"), compiled); - } - if let Some(map) = output.map { - println!("{} {}", colors::bold("map:"), map); - } - if let Some(deps) = output.deps { - println!("{}{}", colors::bold("deps:\n"), deps.name); - if let Some(ref depsdeps) = deps.deps { - for d in depsdeps { - println!("{}", d); - } - } - } else { - println!( - "{} cannot retrieve full dependency graph", - colors::bold("deps:"), - ); - } - Ok(()) - } -} - fn get_types(unstable: bool) -> String { let mut types = format!( "{}\n{}\n{}\n{}", @@ -289,9 +179,15 @@ async fn info_command( print_cache_info(&global_state, json) } else { let main_module = ModuleSpecifier::resolve_url_or_path(&file.unwrap())?; - let mut worker = MainWorker::create(&global_state, main_module.clone())?; - worker.preload_module(&main_module).await?; - print_file_info(&worker, main_module.clone(), json).await + let info = + info::ModuleDepInfo::new(&global_state, main_module.clone()).await?; + + if json { + write_json_to_stdout(&json!(info)) + } else { + print!("{}", info); + Ok(()) + } } } @@ -414,7 +310,7 @@ async fn bundle_command( "{} {:?} ({})", colors::green("Emit"), out_file_, - colors::gray(&human_size(output_len as f64)) + colors::gray(&info::human_size(output_len as f64)) ); } else { println!("{}", output); @@ -422,39 +318,6 @@ async fn bundle_command( Ok(()) } -fn human_size(bytse: f64) -> String { - let negative = if bytse.is_sign_positive() { "" } else { "-" }; - let bytse = bytse.abs(); - let units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - if bytse < 1_f64 { - return format!("{}{} {}", negative, bytse, "Bytes"); - } - let delimiter = 1024_f64; - let exponent = std::cmp::min( - (bytse.ln() / delimiter.ln()).floor() as i32, - (units.len() - 1) as i32, - ); - let pretty_bytes = format!("{:.2}", bytse / delimiter.powi(exponent)) - .parse::() - .unwrap() - * 1_f64; - let unit = units[exponent as usize]; - format!("{}{} {}", negative, pretty_bytes, unit) -} - -#[test] -fn human_size_test() { - assert_eq!(human_size(16_f64), "16 Bytes"); - assert_eq!(human_size((16 * 1024) as f64), "16 KB"); - assert_eq!(human_size((16 * 1024 * 1024) as f64), "16 MB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(3.0)), "16 GB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(4.0)), "16 TB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(5.0)), "16 PB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(6.0)), "16 EB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(7.0)), "16 ZB"); - assert_eq!(human_size(16_f64 * 1024_f64.powf(8.0)), "16 YB"); -} - async fn doc_command( flags: Flags, source_file: Option, diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 331f452416..40147c44c1 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -197,7 +197,7 @@ const SUPPORTED_MEDIA_TYPES: [MediaType; 4] = [ pub type ModuleGraph = HashMap; -#[derive(Debug, Serialize)] +#[derive(Clone, Debug, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ImportDescriptor { pub specifier: String, @@ -241,6 +241,12 @@ pub struct ModuleGraphFile { pub source_code: String, } +impl ModuleGraphFile { + pub fn size(&self) -> usize { + self.source_code.as_bytes().len() + } +} + type SourceFileFuture = Pin>>>; diff --git a/cli/tests/022_info_flag_script.out b/cli/tests/022_info_flag_script.out index d17f31d77b..e84c253e97 100644 --- a/cli/tests/022_info_flag_script.out +++ b/cli/tests/022_info_flag_script.out @@ -1,14 +1,13 @@ [WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] type: TypeScript -compiled: [WILDCARD].js -deps: -http://127.0.0.1:4545/cli/tests/019_media_types.ts - ├── http://localhost:4545/cli/tests/subdir/mt_text_typescript.t1.ts - ├── http://localhost:4545/cli/tests/subdir/mt_video_vdn.t2.ts - ├── http://localhost:4545/cli/tests/subdir/mt_video_mp2t.t3.ts - ├── http://localhost:4545/cli/tests/subdir/mt_application_x_typescript.t4.ts - ├── http://localhost:4545/cli/tests/subdir/mt_text_javascript.j1.js - ├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js - ├── http://localhost:4545/cli/tests/subdir/mt_text_ecmascript.j3.js - └── http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js +deps: 8 unique (total [WILDCARD]) +http://127.0.0.1:4545/cli/tests/019_media_types.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_typescript.t1.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_video_vdn.t2.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_video_mp2t.t3.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_application_x_typescript.t4.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_javascript.j1.js ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_ecmascript.j3.js ([WILDCARD]) +└── http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js ([WILDCARD]) diff --git a/cli/tests/031_info_no_check.out b/cli/tests/031_info_no_check.out index 5b67e3f2f0..6014900558 100644 --- a/cli/tests/031_info_no_check.out +++ b/cli/tests/031_info_no_check.out @@ -1,6 +1,5 @@ [WILDCARD] local: [WILDCARD]031_info_no_check.ts type: TypeScript -compiled: [WILDCARD].js -deps: -[WILDCARD]031_info_no_check.ts +deps: 0 unique (total [WILDCARD]) +[WILDCARD]031_info_no_check.ts ([WILDCARD]) diff --git a/cli/tests/049_info_flag_script_jsx.out b/cli/tests/049_info_flag_script_jsx.out index fd5b511dc9..b5fdd980a2 100644 --- a/cli/tests/049_info_flag_script_jsx.out +++ b/cli/tests/049_info_flag_script_jsx.out @@ -1,14 +1,13 @@ [WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] type: TypeScript -compiled: [WILDCARD].js -deps: -http://127.0.0.1:4545/cli/tests/048_media_types_jsx.ts - ├── http://localhost:4545/cli/tests/subdir/mt_text_typescript_tsx.t1.tsx - ├── http://localhost:4545/cli/tests/subdir/mt_video_vdn_tsx.t2.tsx - ├── http://localhost:4545/cli/tests/subdir/mt_video_mp2t_tsx.t3.tsx - ├── http://localhost:4545/cli/tests/subdir/mt_application_x_typescript_tsx.t4.tsx - ├── http://localhost:4545/cli/tests/subdir/mt_text_javascript_jsx.j1.jsx - ├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript_jsx.j2.jsx - ├── http://localhost:4545/cli/tests/subdir/mt_text_ecmascript_jsx.j3.jsx - └── http://localhost:4545/cli/tests/subdir/mt_application_x_javascript_jsx.j4.jsx +deps: 8 unique (total [WILDCARD]) +http://127.0.0.1:4545/cli/tests/048_media_types_jsx.ts ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_typescript_tsx.t1.tsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_video_vdn_tsx.t2.tsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_video_mp2t_tsx.t3.tsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_application_x_typescript_tsx.t4.tsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_javascript_jsx.j1.jsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript_jsx.j2.jsx ([WILDCARD]) +├── http://localhost:4545/cli/tests/subdir/mt_text_ecmascript_jsx.j3.jsx ([WILDCARD]) +└── http://localhost:4545/cli/tests/subdir/mt_application_x_javascript_jsx.j4.jsx ([WILDCARD]) diff --git a/cli/tests/054_info_local_imports.out b/cli/tests/054_info_local_imports.out index fdf2f44a63..d199dccb3d 100644 --- a/cli/tests/054_info_local_imports.out +++ b/cli/tests/054_info_local_imports.out @@ -1,8 +1,7 @@ local: [WILDCARD]005_more_imports.ts type: TypeScript -compiled: [WILDCARD].js -deps: -file://[WILDCARD]/005_more_imports.ts - └─┬ file://[WILDCARD]/subdir/mod1.ts - └─┬ file://[WILDCARD]/subdir/subdir2/mod2.ts - └── file://[WILDCARD]/subdir/print_hello.ts +deps: 3 unique (total [WILDCARD]) +file://[WILDCARD]/005_more_imports.ts ([WILDCARD]) +└─┬ file://[WILDCARD]/subdir/mod1.ts ([WILDCARD]) + └─┬ file://[WILDCARD]/subdir/subdir2/mod2.ts ([WILDCARD]) + └── file://[WILDCARD]/subdir/print_hello.ts ([WILDCARD]) diff --git a/cli/tests/055_info_file_json.out b/cli/tests/055_info_file_json.out index 08dc737909..5c5da8836e 100644 --- a/cli/tests/055_info_file_json.out +++ b/cli/tests/055_info_file_json.out @@ -1,25 +1,34 @@ { "local": "[WILDCARD]005_more_imports.ts", "fileType": "TypeScript", - "compiled": "[WILDCARD].js", + "compiled": null, "map": null, - "deps": [ - "file://[WILDCARD]/005_more_imports.ts", - [ - [ - "file://[WILDCARD]/subdir/mod1.ts", - [ - [ - "file://[WILDCARD]/subdir/subdir2/mod2.ts", - [ - [ - "file://[WILDCARD]/subdir/print_hello.ts", - [] - ] + "depCount": 3, + "deps": { + "name": "file://[WILDCARD]/005_more_imports.ts", + "size": 211, + "totalSize": 757, + "deps": [ + { + "name": "file://[WILDCARD]/subdir/mod1.ts", + "size": 320, + "totalSize": 546, + "deps": [ + { + "name": "file://[WILDCARD]/subdir/subdir2/mod2.ts", + "size": 163, + "totalSize": 226, + "deps": [ + { + "name": "file://[WILDCARD]/subdir/print_hello.ts", + "size": 63, + "totalSize": 63, + "deps": [] + } ] - ] + } ] - ] + } ] - ] + } } \ No newline at end of file diff --git a/cli/tests/cafile_info.ts.out b/cli/tests/cafile_info.ts.out index f39f381d75..98b82df382 100644 --- a/cli/tests/cafile_info.ts.out +++ b/cli/tests/cafile_info.ts.out @@ -1,13 +1,12 @@ local: [WILDCARD]https[WILDCARD]localhost_PORT5545[WILDCARD] type: TypeScript -compiled: [WILDCARD].js -deps: -https://localhost:5545/cli/tests/cafile_info.ts - ├── https://localhost:5545/cli/tests/subdir/mt_text_typescript.t1.ts - ├── https://localhost:5545/cli/tests/subdir/mt_video_vdn.t2.ts - ├── https://localhost:5545/cli/tests/subdir/mt_video_mp2t.t3.ts - ├── https://localhost:5545/cli/tests/subdir/mt_application_x_typescript.t4.ts - ├── https://localhost:5545/cli/tests/subdir/mt_text_javascript.j1.js - ├── https://localhost:5545/cli/tests/subdir/mt_application_ecmascript.j2.js - ├── https://localhost:5545/cli/tests/subdir/mt_text_ecmascript.j3.js - └── https://localhost:5545/cli/tests/subdir/mt_application_x_javascript.j4.js +deps: 8 unique (total [WILDCARD]) +https://localhost:5545/cli/tests/cafile_info.ts ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_text_typescript.t1.ts ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_video_vdn.t2.ts ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_video_mp2t.t3.ts ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_application_x_typescript.t4.ts ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_text_javascript.j1.js ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_application_ecmascript.j2.js ([WILDCARD]) +├── https://localhost:5545/cli/tests/subdir/mt_text_ecmascript.j3.js ([WILDCARD]) +└── https://localhost:5545/cli/tests/subdir/mt_application_x_javascript.j4.js ([WILDCARD]) diff --git a/cli/tests/info_recursive_imports_test.out b/cli/tests/info_recursive_imports_test.out new file mode 100644 index 0000000000..12fb0e7d30 --- /dev/null +++ b/cli/tests/info_recursive_imports_test.out @@ -0,0 +1,11 @@ +local: [WILDCARD]info_recursive_imports_test.ts +type: TypeScript +deps: 4 unique (total [WILDCARD]) +file://[WILDCARD]cli/tests/info_recursive_imports_test.ts ([WILDCARD]) +└─┬ file://[WILDCARD]cli/tests/recursive_imports/A.ts ([WILDCARD]) + ├─┬ file://[WILDCARD]cli/tests/recursive_imports/B.ts ([WILDCARD]) + │ ├─┬ file://[WILDCARD]cli/tests/recursive_imports/C.ts ([WILDCARD]) + │ │ ├── file://[WILDCARD]cli/tests/recursive_imports/A.ts * + │ │ └── file://[WILDCARD]cli/tests/recursive_imports/common.ts ([WILDCARD]) + │ └── file://[WILDCARD]cli/tests/recursive_imports/common.ts * + └── file://[WILDCARD]cli/tests/recursive_imports/common.ts * diff --git a/cli/tests/info_recursive_imports_test.ts b/cli/tests/info_recursive_imports_test.ts new file mode 100644 index 0000000000..0937bf4a55 --- /dev/null +++ b/cli/tests/info_recursive_imports_test.ts @@ -0,0 +1,5 @@ +import { A } from "./recursive_imports/A.ts"; + +export function test(): void { + A(); +} diff --git a/cli/tests/info_type_import.out b/cli/tests/info_type_import.out index 4096090b34..dc2a3dfe80 100644 --- a/cli/tests/info_type_import.out +++ b/cli/tests/info_type_import.out @@ -1,7 +1,5 @@ -Check [WILDCARD]info_type_import.ts local: [WILDCARD]info_type_import.ts type: TypeScript -compiled: [WILDCARD] -deps: -[WILDCARD]info_type_import.ts - └── [WILDCARD]type_and_code.ts +deps: 1 unique (total [WILDCARD]) +[WILDCARD]info_type_import.ts ([WILDCARD]) +└── [WILDCARD]type_and_code.ts ([WILDCARD]) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 94d410dcc1..4b1b67f7f3 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -949,6 +949,38 @@ fn bundle_import_map() { assert_eq!(output.stderr, b""); } +#[test] +fn info_with_compiled_source() { + let _g = util::http_server(); + let module_path = "http://127.0.0.1:4545/cli/tests/048_media_types_jsx.ts"; + let t = TempDir::new().expect("tempdir fail"); + + let mut deno = util::deno_cmd() + .env("DENO_DIR", t.path()) + .current_dir(util::root_path()) + .arg("cache") + .arg(&module_path) + .spawn() + .expect("failed to spawn script"); + let status = deno.wait().expect("failed to wait for the child process"); + assert!(status.success()); + + let output = util::deno_cmd() + .env("DENO_DIR", t.path()) + .env("NO_COLOR", "1") + .current_dir(util::root_path()) + .arg("info") + .arg(&module_path) + .output() + .expect("failed to spawn script"); + + let str_output = std::str::from_utf8(&output.stdout).unwrap().trim(); + eprintln!("{}", str_output); + // check the output of the test.ts program. + assert!(str_output.contains("compiled: ")); + assert_eq!(output.stderr, b""); +} + #[test] fn repl_test_console_log() { let (out, err) = util::run_and_collect_output( @@ -2289,6 +2321,12 @@ itest!(import_file_with_colon { http_server: true, }); +itest!(info_recursive_modules { + args: "info --quiet info_recursive_imports_test.ts", + output: "info_recursive_imports_test.out", + exit_code: 0, +}); + itest!(info_type_import { args: "info info_type_import.ts", output: "info_type_import.out", diff --git a/cli/tests/recursive_imports/A.ts b/cli/tests/recursive_imports/A.ts new file mode 100644 index 0000000000..04ef61581f --- /dev/null +++ b/cli/tests/recursive_imports/A.ts @@ -0,0 +1,7 @@ +import { B } from "./B.ts"; +import { thing } from "./common.ts"; + +export function A(): void { + thing(); + B(); +} diff --git a/cli/tests/recursive_imports/B.ts b/cli/tests/recursive_imports/B.ts new file mode 100644 index 0000000000..a6e6917fd0 --- /dev/null +++ b/cli/tests/recursive_imports/B.ts @@ -0,0 +1,7 @@ +import { C } from "./C.ts"; +import { thing } from "./common.ts"; + +export function B(): void { + thing(); + C(); +} diff --git a/cli/tests/recursive_imports/C.ts b/cli/tests/recursive_imports/C.ts new file mode 100644 index 0000000000..be6ef92402 --- /dev/null +++ b/cli/tests/recursive_imports/C.ts @@ -0,0 +1,8 @@ +import { A } from "./A.ts"; +import { thing } from "./common.ts"; + +export function C(): void { + if (A != null) { + thing(); + } +} diff --git a/cli/tests/recursive_imports/common.ts b/cli/tests/recursive_imports/common.ts new file mode 100644 index 0000000000..d0f41395d6 --- /dev/null +++ b/cli/tests/recursive_imports/common.ts @@ -0,0 +1,2 @@ +export function thing(): void { +} diff --git a/cli/tsc.rs b/cli/tsc.rs index ec2a5c43e0..0e2e19a9cc 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -570,7 +570,7 @@ impl TsCompiler { source_file: &SourceFile, target: TargetLib, permissions: Permissions, - module_graph: ModuleGraph, + module_graph: &ModuleGraph, allow_js: bool, ) -> Result<(), ErrBox> { let module_url = source_file.url.clone(); @@ -790,7 +790,7 @@ impl TsCompiler { pub async fn transpile( &self, - module_graph: ModuleGraph, + module_graph: &ModuleGraph, ) -> Result<(), ErrBox> { let mut source_files: Vec = Vec::new(); for (_, value) in module_graph.iter() { @@ -1700,7 +1700,7 @@ mod tests { &out, TargetLib::Main, Permissions::allow_all(), - module_graph, + &module_graph, false, ) .await; @@ -1770,7 +1770,7 @@ mod tests { ) .unwrap(); - let result = ts_compiler.transpile(module_graph).await; + let result = ts_compiler.transpile(&module_graph).await; assert!(result.is_ok()); let compiled_file = ts_compiler.get_compiled_module(&out.url).unwrap(); let source_code = compiled_file.code; diff --git a/core/lib.rs b/core/lib.rs index 2c70f0f87e..b27419ff56 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -31,7 +31,6 @@ pub use crate::errors::JsError; pub use crate::flags::v8_set_flags; pub use crate::module_specifier::ModuleResolutionError; pub use crate::module_specifier::ModuleSpecifier; -pub use crate::modules::Deps; pub use crate::modules::ModuleId; pub use crate::modules::ModuleLoadId; pub use crate::modules::ModuleLoader; diff --git a/core/modules.rs b/core/modules.rs index 2b914fd1ec..3caeab7b12 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -8,10 +8,8 @@ use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::Stream; use futures::stream::TryStreamExt; -use serde_json::Value; use std::collections::HashMap; use std::collections::HashSet; -use std::fmt; use std::future::Future; use std::pin::Pin; use std::rc::Rc; @@ -437,108 +435,6 @@ impl Modules { } self.info.get(&id) } - - pub fn deps(&self, module_specifier: &ModuleSpecifier) -> Option { - Deps::new(self, module_specifier) - } -} - -/// This is a tree structure representing the dependencies of a given module. -/// Use Modules::deps to construct it. The 'deps' member is None if this module -/// was already seen elsewhere in the tree. -#[derive(Debug, PartialEq)] -pub struct Deps { - pub name: String, - pub deps: Option>, - prefix: String, - is_last: bool, -} - -impl Deps { - fn new( - modules: &Modules, - module_specifier: &ModuleSpecifier, - ) -> Option { - let mut seen = HashSet::new(); - Self::helper(&mut seen, "".to_string(), true, modules, module_specifier) - } - - fn helper( - seen: &mut HashSet, - prefix: String, - is_last: bool, - modules: &Modules, - module_specifier: &ModuleSpecifier, - ) -> Option { - let name = module_specifier.to_string(); - if seen.contains(&name) { - Some(Deps { - name, - prefix, - deps: None, - is_last, - }) - } else { - let mod_id = modules.get_id(&name)?; - let children = modules.get_children(mod_id).unwrap(); - seen.insert(name.to_string()); - let child_count = children.len(); - let deps: Vec = children - .iter() - .enumerate() - .map(|(index, dep_specifier)| { - let new_is_last = index == child_count - 1; - let mut new_prefix = prefix.clone(); - new_prefix.push(if is_last { ' ' } else { '│' }); - new_prefix.push(' '); - - Self::helper(seen, new_prefix, new_is_last, modules, dep_specifier) - }) - // If any of the children are missing, return None. - .collect::>()?; - - Some(Deps { - name, - prefix, - deps: Some(deps), - is_last, - }) - } - } - - pub fn to_json(&self) -> Value { - let children; - if let Some(deps) = &self.deps { - children = deps.iter().map(|c| c.to_json()).collect(); - } else { - children = Vec::new() - } - serde_json::json!([&self.name, children]) - } -} - -impl fmt::Display for Deps { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut has_children = false; - if let Some(ref deps) = self.deps { - has_children = !deps.is_empty(); - } - write!( - f, - "{}{}─{} {}", - self.prefix, - if self.is_last { "└" } else { "├" }, - if has_children { "┬" } else { "─" }, - self.name - )?; - - if let Some(ref deps) = self.deps { - for d in deps { - write!(f, "\n{}", d)?; - } - } - Ok(()) - } } #[cfg(test)] @@ -1072,56 +968,4 @@ mod tests { ); assert_eq!(modules.get_children(d_id), Some(&vec![])); } - - #[test] - fn empty_deps() { - let modules = Modules::new(); - let specifier = ModuleSpecifier::resolve_url("file:///foo").unwrap(); - assert!(modules.deps(&specifier).is_none()); - } - - #[test] - fn deps_to_json() { - fn dep(name: &str, deps: Option>) -> Deps { - Deps { - name: name.to_string(), - deps, - prefix: "".to_string(), - is_last: false, - } - } - let deps = dep( - "a", - Some(vec![ - dep("b", Some(vec![dep("b2", None)])), - dep("c", Some(vec![])), - ]), - ); - assert_eq!( - serde_json::json!(["a", [["b", [["b2", []]]], ["c", []]]]), - deps.to_json() - ); - } - - /* TODO(bartlomieju): reenable - #[test] - fn deps() { - // "foo" -> "bar" - let mut modules = Modules::new(); - modules.register(1, "foo"); - modules.register(2, "bar"); - modules.add_child(1, "bar"); - let maybe_deps = modules.deps("foo"); - assert!(maybe_deps.is_some()); - let mut foo_deps = maybe_deps.unwrap(); - assert_eq!(foo_deps.name, "foo"); - assert!(foo_deps.deps.is_some()); - let foo_children = foo_deps.deps.take().unwrap(); - assert_eq!(foo_children.len(), 1); - let bar_deps = &foo_children[0]; - assert_eq!(bar_deps.name, "bar"); - assert_eq!(bar_deps.deps, Some(vec![])); - } - - */ } diff --git a/docs/tools/dependency_inspector.md b/docs/tools/dependency_inspector.md index 55e80154e7..80b747b3cf 100644 --- a/docs/tools/dependency_inspector.md +++ b/docs/tools/dependency_inspector.md @@ -3,62 +3,61 @@ `deno info [URL]` will inspect ES module and all of its dependencies. ```shell -deno info https://deno.land/std@0.52.0/http/file_server.ts -Download https://deno.land/std@0.52.0/http/file_server.ts +deno info https://deno.land/std@0.67.0/http/file_server.ts +Download https://deno.land/std@0.67.0/http/file_server.ts ... -local: /Users/deno/Library/Caches/deno/deps/https/deno.land/5bd138988e9d20db1a436666628ffb3f7586934e0a2a9fe2a7b7bf4fb7f70b98 +local: /home/deno/.cache/deno/deps/https/deno.land/f57792e36f2dbf28b14a75e2372a479c6392780d4712d76698d5031f943c0020 type: TypeScript -compiled: /Users/deno/Library/Caches/deno/gen/https/deno.land/std@0.52.0/http/file_server.ts.js -map: /Users/deno/Library/Caches/deno/gen/https/deno.land/std@0.52.0/http/file_server.ts.js.map -deps: -https://deno.land/std@0.52.0/http/file_server.ts - ├─┬ https://deno.land/std@0.52.0/path/mod.ts - │ ├─┬ https://deno.land/std@0.52.0/path/win32.ts - │ │ ├── https://deno.land/std@0.52.0/path/_constants.ts - │ │ ├─┬ https://deno.land/std@0.52.0/path/_util.ts - │ │ │ └── https://deno.land/std@0.52.0/path/_constants.ts - │ │ └─┬ https://deno.land/std@0.52.0/testing/asserts.ts - │ │ ├── https://deno.land/std@0.52.0/fmt/colors.ts - │ │ └── https://deno.land/std@0.52.0/testing/diff.ts - │ ├─┬ https://deno.land/std@0.52.0/path/posix.ts - │ │ ├── https://deno.land/std@0.52.0/path/_constants.ts - │ │ └── https://deno.land/std@0.52.0/path/_util.ts - │ ├─┬ https://deno.land/std@0.52.0/path/common.ts - │ │ └── https://deno.land/std@0.52.0/path/separator.ts - │ ├── https://deno.land/std@0.52.0/path/separator.ts - │ ├── https://deno.land/std@0.52.0/path/interface.ts - │ └─┬ https://deno.land/std@0.52.0/path/glob.ts - │ ├── https://deno.land/std@0.52.0/path/separator.ts - │ ├── https://deno.land/std@0.52.0/path/_globrex.ts - │ ├── https://deno.land/std@0.52.0/path/mod.ts - │ └── https://deno.land/std@0.52.0/testing/asserts.ts - ├─┬ https://deno.land/std@0.52.0/http/server.ts - │ ├── https://deno.land/std@0.52.0/encoding/utf8.ts - │ ├─┬ https://deno.land/std@0.52.0/io/bufio.ts - │ │ ├─┬ https://deno.land/std@0.52.0/io/util.ts - │ │ │ ├── https://deno.land/std@0.52.0/path/mod.ts - │ │ │ └── https://deno.land/std@0.52.0/encoding/utf8.ts - │ │ └── https://deno.land/std@0.52.0/testing/asserts.ts - │ ├── https://deno.land/std@0.52.0/testing/asserts.ts - │ ├─┬ https://deno.land/std@0.52.0/async/mod.ts - │ │ ├── https://deno.land/std@0.52.0/async/deferred.ts - │ │ ├── https://deno.land/std@0.52.0/async/delay.ts - │ │ └─┬ https://deno.land/std@0.52.0/async/mux_async_iterator.ts - │ │ └── https://deno.land/std@0.52.0/async/deferred.ts - │ └─┬ https://deno.land/std@0.52.0/http/_io.ts - │ ├── https://deno.land/std@0.52.0/io/bufio.ts - │ ├─┬ https://deno.land/std@0.52.0/textproto/mod.ts - │ │ ├── https://deno.land/std@0.52.0/io/util.ts - │ │ ├─┬ https://deno.land/std@0.52.0/bytes/mod.ts - │ │ │ └── https://deno.land/std@0.52.0/io/util.ts - │ │ └── https://deno.land/std@0.52.0/encoding/utf8.ts - │ ├── https://deno.land/std@0.52.0/testing/asserts.ts - │ ├── https://deno.land/std@0.52.0/encoding/utf8.ts - │ ├── https://deno.land/std@0.52.0/http/server.ts - │ └── https://deno.land/std@0.52.0/http/http_status.ts - ├─┬ https://deno.land/std@0.52.0/flags/mod.ts - │ └── https://deno.land/std@0.52.0/testing/asserts.ts - └── https://deno.land/std@0.52.0/testing/asserts.ts +compiled: /home/deno/.cache/deno/gen/https/deno.land/f57792e36f2dbf28b14a75e2372a479c6392780d4712d76698d5031f943c0020.js +deps: 23 unique (total 139.89KB) +https://deno.land/std@0.67.0/http/file_server.ts (10.49KB) +├─┬ https://deno.land/std@0.67.0/path/mod.ts (717B) +│ ├── https://deno.land/std@0.67.0/path/_constants.ts (2.35KB) +│ ├─┬ https://deno.land/std@0.67.0/path/win32.ts (27.36KB) +│ │ ├── https://deno.land/std@0.67.0/path/_interface.ts (657B) +│ │ ├── https://deno.land/std@0.67.0/path/_constants.ts * +│ │ ├─┬ https://deno.land/std@0.67.0/path/_util.ts (3.3KB) +│ │ │ ├── https://deno.land/std@0.67.0/path/_interface.ts * +│ │ │ └── https://deno.land/std@0.67.0/path/_constants.ts * +│ │ └── https://deno.land/std@0.67.0/_util/assert.ts (405B) +│ ├─┬ https://deno.land/std@0.67.0/path/posix.ts (12.67KB) +│ │ ├── https://deno.land/std@0.67.0/path/_interface.ts * +│ │ ├── https://deno.land/std@0.67.0/path/_constants.ts * +│ │ └── https://deno.land/std@0.67.0/path/_util.ts * +│ ├─┬ https://deno.land/std@0.67.0/path/common.ts (1.14KB) +│ │ └─┬ https://deno.land/std@0.67.0/path/separator.ts (264B) +│ │ └── https://deno.land/std@0.67.0/path/_constants.ts * +│ ├── https://deno.land/std@0.67.0/path/separator.ts * +│ ├── https://deno.land/std@0.67.0/path/_interface.ts * +│ └─┬ https://deno.land/std@0.67.0/path/glob.ts (8.12KB) +│ ├── https://deno.land/std@0.67.0/path/_constants.ts * +│ ├── https://deno.land/std@0.67.0/path/mod.ts * +│ └── https://deno.land/std@0.67.0/path/separator.ts * +├─┬ https://deno.land/std@0.67.0/http/server.ts (10.23KB) +│ ├── https://deno.land/std@0.67.0/encoding/utf8.ts (433B) +│ ├─┬ https://deno.land/std@0.67.0/io/bufio.ts (21.15KB) +│ │ ├── https://deno.land/std@0.67.0/bytes/mod.ts (4.34KB) +│ │ └── https://deno.land/std@0.67.0/_util/assert.ts * +│ ├── https://deno.land/std@0.67.0/_util/assert.ts * +│ ├─┬ https://deno.land/std@0.67.0/async/mod.ts (202B) +│ │ ├── https://deno.land/std@0.67.0/async/deferred.ts (1.03KB) +│ │ ├── https://deno.land/std@0.67.0/async/delay.ts (279B) +│ │ ├─┬ https://deno.land/std@0.67.0/async/mux_async_iterator.ts (1.98KB) +│ │ │ └── https://deno.land/std@0.67.0/async/deferred.ts * +│ │ └── https://deno.land/std@0.67.0/async/pool.ts (1.58KB) +│ └─┬ https://deno.land/std@0.67.0/http/_io.ts (11.25KB) +│ ├── https://deno.land/std@0.67.0/io/bufio.ts * +│ ├─┬ https://deno.land/std@0.67.0/textproto/mod.ts (4.52KB) +│ │ ├── https://deno.land/std@0.67.0/io/bufio.ts * +│ │ ├── https://deno.land/std@0.67.0/bytes/mod.ts * +│ │ └── https://deno.land/std@0.67.0/encoding/utf8.ts * +│ ├── https://deno.land/std@0.67.0/_util/assert.ts * +│ ├── https://deno.land/std@0.67.0/encoding/utf8.ts * +│ ├── https://deno.land/std@0.67.0/http/server.ts * +│ └── https://deno.land/std@0.67.0/http/http_status.ts (5.93KB) +├─┬ https://deno.land/std@0.67.0/flags/mod.ts (9.54KB) +│ └── https://deno.land/std@0.67.0/_util/assert.ts * +└── https://deno.land/std@0.67.0/_util/assert.ts * ``` Dependency inspector works with any local or remote ES modules.