mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
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
This commit is contained in:
parent
7a8b27aa25
commit
1cd2267500
22 changed files with 692 additions and 430 deletions
|
@ -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?;
|
||||
}
|
||||
}
|
||||
|
|
471
cli/info.rs
Normal file
471
cli/info.rs
Normal file
|
@ -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<String>,
|
||||
map: Option<String>,
|
||||
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<GlobalState>,
|
||||
module_specifier: ModuleSpecifier,
|
||||
) -> Result<ModuleDepInfo, ErrBox> {
|
||||
// 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<usize>,
|
||||
deps: Vec<FileInfoDepTree>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
total_sizes: &mut HashMap<String, usize>,
|
||||
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::<Vec<_>>();
|
||||
|
||||
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::<f64>()
|
||||
.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<ModuleSpecifier>,
|
||||
redirect: Option<ModuleSpecifier>,
|
||||
) -> (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);
|
||||
}
|
||||
}
|
159
cli/main.rs
159
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<String>,
|
||||
map: Option<String>,
|
||||
deps: Option<Deps>,
|
||||
}
|
||||
|
||||
// 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::<f64>()
|
||||
.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<String>,
|
||||
|
|
|
@ -197,7 +197,7 @@ const SUPPORTED_MEDIA_TYPES: [MediaType; 4] = [
|
|||
|
||||
pub type ModuleGraph = HashMap<String, ModuleGraphFile>;
|
||||
|
||||
#[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<Box<dyn Future<Output = Result<(ModuleSpecifier, SourceFile), ErrBox>>>>;
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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])
|
||||
|
|
11
cli/tests/info_recursive_imports_test.out
Normal file
11
cli/tests/info_recursive_imports_test.out
Normal file
|
@ -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 *
|
5
cli/tests/info_recursive_imports_test.ts
Normal file
5
cli/tests/info_recursive_imports_test.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { A } from "./recursive_imports/A.ts";
|
||||
|
||||
export function test(): void {
|
||||
A();
|
||||
}
|
|
@ -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])
|
||||
|
|
|
@ -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",
|
||||
|
|
7
cli/tests/recursive_imports/A.ts
Normal file
7
cli/tests/recursive_imports/A.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { B } from "./B.ts";
|
||||
import { thing } from "./common.ts";
|
||||
|
||||
export function A(): void {
|
||||
thing();
|
||||
B();
|
||||
}
|
7
cli/tests/recursive_imports/B.ts
Normal file
7
cli/tests/recursive_imports/B.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { C } from "./C.ts";
|
||||
import { thing } from "./common.ts";
|
||||
|
||||
export function B(): void {
|
||||
thing();
|
||||
C();
|
||||
}
|
8
cli/tests/recursive_imports/C.ts
Normal file
8
cli/tests/recursive_imports/C.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { A } from "./A.ts";
|
||||
import { thing } from "./common.ts";
|
||||
|
||||
export function C(): void {
|
||||
if (A != null) {
|
||||
thing();
|
||||
}
|
||||
}
|
2
cli/tests/recursive_imports/common.ts
Normal file
2
cli/tests/recursive_imports/common.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export function thing(): void {
|
||||
}
|
|
@ -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<TranspileSourceFile> = 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
156
core/modules.rs
156
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> {
|
||||
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<Vec<Deps>>,
|
||||
prefix: String,
|
||||
is_last: bool,
|
||||
}
|
||||
|
||||
impl Deps {
|
||||
fn new(
|
||||
modules: &Modules,
|
||||
module_specifier: &ModuleSpecifier,
|
||||
) -> Option<Deps> {
|
||||
let mut seen = HashSet::new();
|
||||
Self::helper(&mut seen, "".to_string(), true, modules, module_specifier)
|
||||
}
|
||||
|
||||
fn helper(
|
||||
seen: &mut HashSet<String>,
|
||||
prefix: String,
|
||||
is_last: bool,
|
||||
modules: &Modules,
|
||||
module_specifier: &ModuleSpecifier,
|
||||
) -> Option<Deps> {
|
||||
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<Deps> = 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::<Option<_>>()?;
|
||||
|
||||
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<Vec<Deps>>) -> 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![]));
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue