mirror of
https://github.com/denoland/deno.git
synced 2025-01-18 11:53:59 -05:00
refactor: move deno info
functionality from deno_graph to CLI (#16434)
Closes #16423
This commit is contained in:
parent
ec09134d8a
commit
56d5cb21b0
5 changed files with 718 additions and 124 deletions
|
@ -1,5 +1,9 @@
|
||||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::serde_json;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
/// A function that converts a float to a string the represents a human
|
/// A function that converts a float to a string the represents a human
|
||||||
/// readable version of that number.
|
/// readable version of that number.
|
||||||
pub fn human_size(size: f64) -> String {
|
pub fn human_size(size: f64) -> String {
|
||||||
|
@ -38,6 +42,30 @@ pub fn human_elapsed(elapsed: u128) -> String {
|
||||||
format!("{}m{}s", minutes, seconds_remainder)
|
format!("{}m{}s", minutes, seconds_remainder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_to_stdout_ignore_sigpipe(
|
||||||
|
bytes: &[u8],
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
|
match std::io::stdout().write_all(bytes) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
ErrorKind::BrokenPipe => Ok(()),
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_json_to_stdout<T>(value: &T) -> Result<(), AnyError>
|
||||||
|
where
|
||||||
|
T: ?Sized + serde::ser::Serialize,
|
||||||
|
{
|
||||||
|
let mut writer = std::io::BufWriter::new(std::io::stdout());
|
||||||
|
serde_json::to_writer_pretty(&mut writer, value)?;
|
||||||
|
writeln!(&mut writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
125
cli/main.rs
125
cli/main.rs
|
@ -84,8 +84,6 @@ use deno_core::futures::future::FutureExt;
|
||||||
use deno_core::futures::Future;
|
use deno_core::futures::Future;
|
||||||
use deno_core::parking_lot::RwLock;
|
use deno_core::parking_lot::RwLock;
|
||||||
use deno_core::resolve_url_or_path;
|
use deno_core::resolve_url_or_path;
|
||||||
use deno_core::serde_json;
|
|
||||||
use deno_core::serde_json::json;
|
|
||||||
use deno_core::v8_set_flags;
|
use deno_core::v8_set_flags;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_runtime::colors;
|
use deno_runtime::colors;
|
||||||
|
@ -97,114 +95,12 @@ use log::info;
|
||||||
use npm::NpmPackageReference;
|
use npm::NpmPackageReference;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use worker::create_main_worker;
|
use worker::create_main_worker;
|
||||||
|
|
||||||
pub fn write_to_stdout_ignore_sigpipe(
|
|
||||||
bytes: &[u8],
|
|
||||||
) -> Result<(), std::io::Error> {
|
|
||||||
use std::io::ErrorKind;
|
|
||||||
|
|
||||||
match std::io::stdout().write_all(bytes) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => match e.kind() {
|
|
||||||
ErrorKind::BrokenPipe => Ok(()),
|
|
||||||
_ => Err(e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_json_to_stdout<T>(value: &T) -> Result<(), AnyError>
|
|
||||||
where
|
|
||||||
T: ?Sized + serde::ser::Serialize,
|
|
||||||
{
|
|
||||||
let mut writer = std::io::BufWriter::new(std::io::stdout());
|
|
||||||
serde_json::to_writer_pretty(&mut writer, value)?;
|
|
||||||
writeln!(&mut writer)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_cache_info(
|
|
||||||
state: &ProcState,
|
|
||||||
json: bool,
|
|
||||||
location: Option<&deno_core::url::Url>,
|
|
||||||
) -> Result<(), AnyError> {
|
|
||||||
let deno_dir = &state.dir.root;
|
|
||||||
let modules_cache = &state.file_fetcher.get_http_cache_location();
|
|
||||||
let npm_cache = &state.npm_cache.as_readonly().get_cache_location();
|
|
||||||
let typescript_cache = &state.dir.gen_cache.location;
|
|
||||||
let registry_cache =
|
|
||||||
&state.dir.root.join(lsp::language_server::REGISTRIES_PATH);
|
|
||||||
let mut origin_dir = state.dir.root.join("location_data");
|
|
||||||
|
|
||||||
if let Some(location) = &location {
|
|
||||||
origin_dir =
|
|
||||||
origin_dir.join(&checksum::gen(&[location.to_string().as_bytes()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
let local_storage_dir = origin_dir.join("local_storage");
|
|
||||||
|
|
||||||
if json {
|
|
||||||
let mut output = json!({
|
|
||||||
"denoDir": deno_dir,
|
|
||||||
"modulesCache": modules_cache,
|
|
||||||
"npmCache": npm_cache,
|
|
||||||
"typescriptCache": typescript_cache,
|
|
||||||
"registryCache": registry_cache,
|
|
||||||
"originStorage": origin_dir,
|
|
||||||
});
|
|
||||||
|
|
||||||
if location.is_some() {
|
|
||||||
output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_json_to_stdout(&output)
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("DENO_DIR location:"),
|
|
||||||
deno_dir.display()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("Remote modules cache:"),
|
|
||||||
modules_cache.display()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("npm modules cache:"),
|
|
||||||
npm_cache.display()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("Emitted modules cache:"),
|
|
||||||
typescript_cache.display()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("Language server registries cache:"),
|
|
||||||
registry_cache.display(),
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("Origin storage:"),
|
|
||||||
origin_dir.display()
|
|
||||||
);
|
|
||||||
if location.is_some() {
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
colors::bold("Local Storage:"),
|
|
||||||
local_storage_dir.display(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_types(unstable: bool) -> String {
|
pub fn get_types(unstable: bool) -> String {
|
||||||
let mut types = vec![
|
let mut types = vec![
|
||||||
tsc::DENO_NS_LIB,
|
tsc::DENO_NS_LIB,
|
||||||
|
@ -314,22 +210,7 @@ async fn info_command(
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
info_flags: InfoFlags,
|
info_flags: InfoFlags,
|
||||||
) -> Result<i32, AnyError> {
|
) -> Result<i32, AnyError> {
|
||||||
let ps = ProcState::build(flags).await?;
|
tools::info::info(flags, info_flags).await?;
|
||||||
if let Some(specifier) = info_flags.file {
|
|
||||||
let specifier = resolve_url_or_path(&specifier)?;
|
|
||||||
let graph = ps
|
|
||||||
.create_graph(vec![(specifier, deno_graph::ModuleKind::Esm)])
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if info_flags.json {
|
|
||||||
write_json_to_stdout(&json!(graph))?;
|
|
||||||
} else {
|
|
||||||
write_to_stdout_ignore_sigpipe(graph.to_string().as_bytes())?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If it was just "deno info" print location of caches and exit
|
|
||||||
print_cache_info(&ps, info_flags.json, ps.options.location_flag())?;
|
|
||||||
}
|
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -926,13 +807,13 @@ async fn completions_command(
|
||||||
_flags: Flags,
|
_flags: Flags,
|
||||||
completions_flags: CompletionsFlags,
|
completions_flags: CompletionsFlags,
|
||||||
) -> Result<i32, AnyError> {
|
) -> Result<i32, AnyError> {
|
||||||
write_to_stdout_ignore_sigpipe(&completions_flags.buf)?;
|
display::write_to_stdout_ignore_sigpipe(&completions_flags.buf)?;
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn types_command(flags: Flags) -> Result<i32, AnyError> {
|
async fn types_command(flags: Flags) -> Result<i32, AnyError> {
|
||||||
let types = get_types(flags.unstable);
|
let types = get_types(flags.unstable);
|
||||||
write_to_stdout_ignore_sigpipe(types.as_bytes())?;
|
display::write_to_stdout_ignore_sigpipe(types.as_bytes())?;
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
use crate::args::DocFlags;
|
use crate::args::DocFlags;
|
||||||
use crate::args::Flags;
|
use crate::args::Flags;
|
||||||
use crate::colors;
|
use crate::colors;
|
||||||
|
use crate::display::write_json_to_stdout;
|
||||||
|
use crate::display::write_to_stdout_ignore_sigpipe;
|
||||||
use crate::file_fetcher::File;
|
use crate::file_fetcher::File;
|
||||||
use crate::get_types;
|
use crate::get_types;
|
||||||
use crate::proc_state::ProcState;
|
use crate::proc_state::ProcState;
|
||||||
use crate::write_json_to_stdout;
|
|
||||||
use crate::write_to_stdout_ignore_sigpipe;
|
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
|
684
cli/tools/info.rs
Normal file
684
cli/tools/info.rs
Normal file
|
@ -0,0 +1,684 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use deno_ast::ModuleSpecifier;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::resolve_url_or_path;
|
||||||
|
use deno_core::serde_json;
|
||||||
|
use deno_core::serde_json::json;
|
||||||
|
use deno_graph::Dependency;
|
||||||
|
use deno_graph::Module;
|
||||||
|
use deno_graph::ModuleGraph;
|
||||||
|
use deno_graph::ModuleGraphError;
|
||||||
|
use deno_graph::ModuleKind;
|
||||||
|
use deno_graph::Resolved;
|
||||||
|
use deno_runtime::colors;
|
||||||
|
|
||||||
|
use crate::args::Flags;
|
||||||
|
use crate::args::InfoFlags;
|
||||||
|
use crate::checksum;
|
||||||
|
use crate::display;
|
||||||
|
use crate::lsp;
|
||||||
|
use crate::proc_state::ProcState;
|
||||||
|
|
||||||
|
pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> {
|
||||||
|
let ps = ProcState::build(flags).await?;
|
||||||
|
if let Some(specifier) = info_flags.file {
|
||||||
|
let specifier = resolve_url_or_path(&specifier)?;
|
||||||
|
let graph = ps.create_graph(vec![(specifier, ModuleKind::Esm)]).await?;
|
||||||
|
|
||||||
|
if info_flags.json {
|
||||||
|
display::write_json_to_stdout(&json!(graph))?;
|
||||||
|
} else {
|
||||||
|
let mut output = String::new();
|
||||||
|
fmt_module_graph(&graph, &mut output)?;
|
||||||
|
display::write_to_stdout_ignore_sigpipe(output.as_bytes())?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If it was just "deno info" print location of caches and exit
|
||||||
|
print_cache_info(&ps, info_flags.json, ps.options.location_flag())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_cache_info(
|
||||||
|
state: &ProcState,
|
||||||
|
json: bool,
|
||||||
|
location: Option<&deno_core::url::Url>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let deno_dir = &state.dir.root;
|
||||||
|
let modules_cache = &state.file_fetcher.get_http_cache_location();
|
||||||
|
let npm_cache = &state.npm_cache.as_readonly().get_cache_location();
|
||||||
|
let typescript_cache = &state.dir.gen_cache.location;
|
||||||
|
let registry_cache =
|
||||||
|
&state.dir.root.join(lsp::language_server::REGISTRIES_PATH);
|
||||||
|
let mut origin_dir = state.dir.root.join("location_data");
|
||||||
|
|
||||||
|
if let Some(location) = &location {
|
||||||
|
origin_dir =
|
||||||
|
origin_dir.join(&checksum::gen(&[location.to_string().as_bytes()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_storage_dir = origin_dir.join("local_storage");
|
||||||
|
|
||||||
|
if json {
|
||||||
|
let mut output = json!({
|
||||||
|
"denoDir": deno_dir,
|
||||||
|
"modulesCache": modules_cache,
|
||||||
|
"npmCache": npm_cache,
|
||||||
|
"typescriptCache": typescript_cache,
|
||||||
|
"registryCache": registry_cache,
|
||||||
|
"originStorage": origin_dir,
|
||||||
|
});
|
||||||
|
|
||||||
|
if location.is_some() {
|
||||||
|
output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
display::write_json_to_stdout(&output)
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("DENO_DIR location:"),
|
||||||
|
deno_dir.display()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("Remote modules cache:"),
|
||||||
|
modules_cache.display()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("npm modules cache:"),
|
||||||
|
npm_cache.display()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("Emitted modules cache:"),
|
||||||
|
typescript_cache.display()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("Language server registries cache:"),
|
||||||
|
registry_cache.display(),
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("Origin storage:"),
|
||||||
|
origin_dir.display()
|
||||||
|
);
|
||||||
|
if location.is_some() {
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("Local Storage:"),
|
||||||
|
local_storage_dir.display(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIBLING_CONNECTOR: char = '├';
|
||||||
|
const LAST_SIBLING_CONNECTOR: char = '└';
|
||||||
|
const CHILD_DEPS_CONNECTOR: char = '┬';
|
||||||
|
const CHILD_NO_DEPS_CONNECTOR: char = '─';
|
||||||
|
const VERTICAL_CONNECTOR: char = '│';
|
||||||
|
const EMPTY_CONNECTOR: char = ' ';
|
||||||
|
|
||||||
|
fn fmt_module_graph(graph: &ModuleGraph, f: &mut impl Write) -> fmt::Result {
|
||||||
|
if graph.roots.is_empty() || graph.roots.len() > 1 {
|
||||||
|
return writeln!(
|
||||||
|
f,
|
||||||
|
"{} displaying graphs that have multiple roots is not supported.",
|
||||||
|
colors::red("error:")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let root_specifier = graph.resolve(&graph.roots[0].0);
|
||||||
|
match graph.try_get(&root_specifier) {
|
||||||
|
Ok(Some(root)) => {
|
||||||
|
if let Some(cache_info) = root.maybe_cache_info.as_ref() {
|
||||||
|
if let Some(local) = &cache_info.local {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{} {}",
|
||||||
|
colors::bold("local:"),
|
||||||
|
local.to_string_lossy()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if let Some(emit) = &cache_info.emit {
|
||||||
|
writeln!(f, "{} {}", colors::bold("emit:"), emit.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
if let Some(map) = &cache_info.map {
|
||||||
|
writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f, "{} {}", colors::bold("type:"), root.media_type)?;
|
||||||
|
let modules = graph.modules();
|
||||||
|
let total_size: f64 = modules.iter().map(|m| m.size() as f64).sum();
|
||||||
|
let dep_count = modules.len() - 1;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{} {} unique {}",
|
||||||
|
colors::bold("dependencies:"),
|
||||||
|
dep_count,
|
||||||
|
colors::gray(format!("(total {})", display::human_size(total_size)))
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"\n{} {}",
|
||||||
|
root_specifier,
|
||||||
|
colors::gray(format!("({})", display::human_size(root.size() as f64)))
|
||||||
|
)?;
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
let dep_len = root.dependencies.len();
|
||||||
|
for (idx, (_, dep)) in root.dependencies.iter().enumerate() {
|
||||||
|
fmt_dep_info(
|
||||||
|
dep,
|
||||||
|
f,
|
||||||
|
"",
|
||||||
|
idx == dep_len - 1 && root.maybe_types_dependency.is_none(),
|
||||||
|
graph,
|
||||||
|
&mut seen,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(ModuleGraphError::Missing(_)) => {
|
||||||
|
writeln!(f, "{} module could not be found", colors::red("error:"))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
writeln!(f, "{} {}", colors::red("error:"), err)
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
writeln!(f, "{} an internal error occurred", colors::red("error:"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dep_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||||
|
dep: &Dependency,
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
graph: &ModuleGraph,
|
||||||
|
seen: &mut HashSet<ModuleSpecifier>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
if !dep.maybe_code.is_none() {
|
||||||
|
fmt_resolved_info(
|
||||||
|
&dep.maybe_code,
|
||||||
|
f,
|
||||||
|
prefix.clone(),
|
||||||
|
dep.maybe_type.is_none() && last,
|
||||||
|
graph,
|
||||||
|
false,
|
||||||
|
seen,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if !dep.maybe_type.is_none() {
|
||||||
|
fmt_resolved_info(&dep.maybe_type, f, prefix, last, graph, true, seen)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_module_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||||
|
module: &Module,
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
graph: &ModuleGraph,
|
||||||
|
type_dep: bool,
|
||||||
|
seen: &mut HashSet<ModuleSpecifier>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
let was_seen = seen.contains(&module.specifier);
|
||||||
|
let children = !((module.dependencies.is_empty()
|
||||||
|
&& module.maybe_types_dependency.is_none())
|
||||||
|
|| was_seen);
|
||||||
|
let (specifier_str, size_str) = if was_seen {
|
||||||
|
let specifier_str = if type_dep {
|
||||||
|
colors::italic_gray(&module.specifier).to_string()
|
||||||
|
} else {
|
||||||
|
colors::gray(&module.specifier).to_string()
|
||||||
|
};
|
||||||
|
(specifier_str, colors::gray(" *").to_string())
|
||||||
|
} else {
|
||||||
|
let specifier_str = if type_dep {
|
||||||
|
colors::italic(&module.specifier).to_string()
|
||||||
|
} else {
|
||||||
|
module.specifier.to_string()
|
||||||
|
};
|
||||||
|
let size_str =
|
||||||
|
colors::gray(format!(" ({})", display::human_size(module.size() as f64)))
|
||||||
|
.to_string();
|
||||||
|
(specifier_str, size_str)
|
||||||
|
};
|
||||||
|
|
||||||
|
seen.insert(module.specifier.clone());
|
||||||
|
|
||||||
|
fmt_info_msg(
|
||||||
|
f,
|
||||||
|
prefix.clone(),
|
||||||
|
last,
|
||||||
|
children,
|
||||||
|
format!("{}{}", specifier_str, size_str),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if !was_seen {
|
||||||
|
let mut prefix = prefix.to_string();
|
||||||
|
if last {
|
||||||
|
prefix.push(EMPTY_CONNECTOR);
|
||||||
|
} else {
|
||||||
|
prefix.push(VERTICAL_CONNECTOR);
|
||||||
|
}
|
||||||
|
prefix.push(EMPTY_CONNECTOR);
|
||||||
|
let dep_len = module.dependencies.len();
|
||||||
|
if let Some((_, type_dep)) = &module.maybe_types_dependency {
|
||||||
|
fmt_resolved_info(type_dep, f, &prefix, dep_len == 0, graph, true, seen)?;
|
||||||
|
}
|
||||||
|
for (idx, (_, dep)) in module.dependencies.iter().enumerate() {
|
||||||
|
fmt_dep_info(
|
||||||
|
dep,
|
||||||
|
f,
|
||||||
|
&prefix,
|
||||||
|
idx == dep_len - 1 && module.maybe_types_dependency.is_none(),
|
||||||
|
graph,
|
||||||
|
seen,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_error_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||||
|
err: &ModuleGraphError,
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
seen: &mut HashSet<ModuleSpecifier>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
seen.insert(specifier.clone());
|
||||||
|
match err {
|
||||||
|
ModuleGraphError::InvalidSource(_, _) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(invalid source)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::InvalidTypeAssertion { .. } => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(invalid import assertion)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::LoadingErr(_, _) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(loading error)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::ParseErr(_, _) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(parsing error)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::ResolutionError(_) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(resolution error)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::UnsupportedImportAssertionType(_, _) => fmt_error_msg(
|
||||||
|
f,
|
||||||
|
prefix,
|
||||||
|
last,
|
||||||
|
specifier,
|
||||||
|
"(unsupported import assertion)",
|
||||||
|
),
|
||||||
|
ModuleGraphError::UnsupportedMediaType(_, _) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(unsupported)")
|
||||||
|
}
|
||||||
|
ModuleGraphError::Missing(_) => {
|
||||||
|
fmt_error_msg(f, prefix, last, specifier, "(missing)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_info_msg<S, M>(
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
children: bool,
|
||||||
|
msg: M,
|
||||||
|
) -> fmt::Result
|
||||||
|
where
|
||||||
|
S: AsRef<str> + fmt::Display + Clone,
|
||||||
|
M: AsRef<str> + fmt::Display,
|
||||||
|
{
|
||||||
|
let sibling_connector = if last {
|
||||||
|
LAST_SIBLING_CONNECTOR
|
||||||
|
} else {
|
||||||
|
SIBLING_CONNECTOR
|
||||||
|
};
|
||||||
|
let child_connector = if children {
|
||||||
|
CHILD_DEPS_CONNECTOR
|
||||||
|
} else {
|
||||||
|
CHILD_NO_DEPS_CONNECTOR
|
||||||
|
};
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{} {}",
|
||||||
|
colors::gray(format!(
|
||||||
|
"{}{}─{}",
|
||||||
|
prefix, sibling_connector, child_connector
|
||||||
|
)),
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_error_msg<S, M>(
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
error_msg: M,
|
||||||
|
) -> fmt::Result
|
||||||
|
where
|
||||||
|
S: AsRef<str> + fmt::Display + Clone,
|
||||||
|
M: AsRef<str> + fmt::Display,
|
||||||
|
{
|
||||||
|
fmt_info_msg(
|
||||||
|
f,
|
||||||
|
prefix,
|
||||||
|
last,
|
||||||
|
false,
|
||||||
|
format!("{} {}", colors::red(specifier), colors::red_bold(error_msg)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_resolved_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||||
|
resolved: &Resolved,
|
||||||
|
f: &mut impl Write,
|
||||||
|
prefix: S,
|
||||||
|
last: bool,
|
||||||
|
graph: &ModuleGraph,
|
||||||
|
type_dep: bool,
|
||||||
|
seen: &mut HashSet<ModuleSpecifier>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
match resolved {
|
||||||
|
Resolved::Ok { specifier, .. } => {
|
||||||
|
let resolved_specifier = graph.resolve(specifier);
|
||||||
|
match graph.try_get(&resolved_specifier) {
|
||||||
|
Ok(Some(module)) => {
|
||||||
|
fmt_module_info(module, f, prefix, last, graph, type_dep, seen)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
fmt_error_info(&err, f, prefix, last, &resolved_specifier, seen)
|
||||||
|
}
|
||||||
|
Ok(None) => fmt_info_msg(
|
||||||
|
f,
|
||||||
|
prefix,
|
||||||
|
last,
|
||||||
|
false,
|
||||||
|
format!(
|
||||||
|
"{} {}",
|
||||||
|
colors::red(specifier),
|
||||||
|
colors::red_bold("(missing)")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Resolved::Err(err) => fmt_info_msg(
|
||||||
|
f,
|
||||||
|
prefix,
|
||||||
|
last,
|
||||||
|
false,
|
||||||
|
format!(
|
||||||
|
"{} {}",
|
||||||
|
colors::italic(err.to_string()),
|
||||||
|
colors::red_bold("(resolve error)")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use deno_graph::source::CacheInfo;
|
||||||
|
use deno_graph::source::MemoryLoader;
|
||||||
|
use deno_graph::source::Source;
|
||||||
|
use test_util::strip_ansi_codes;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_info_graph() {
|
||||||
|
let mut loader = MemoryLoader::new(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/a.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/a.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"import * as b from "./b.ts";
|
||||||
|
import type { F } from "./f.d.ts";
|
||||||
|
import * as g from "./g.js";
|
||||||
|
"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/b.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/b.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"
|
||||||
|
// @deno-types="./c.d.ts"
|
||||||
|
import * as c from "./c.js";
|
||||||
|
import * as d from "./d.ts";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/c.js",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/c.js",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/javascript",
|
||||||
|
)]),
|
||||||
|
content: r#"export const c = "c";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/c.d.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/c.d.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"export const c: "c";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/d.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/d.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"import * as e from "./e.ts";
|
||||||
|
export const d = "d";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/e.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/e.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"import * as b from "./b.ts";
|
||||||
|
export const e = "e";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/f.d.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/f.d.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"export interface F { }"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/g.js",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/g.js",
|
||||||
|
maybe_headers: Some(vec![
|
||||||
|
("content-type", "application/javascript"),
|
||||||
|
("x-typescript-types", "./g.d.ts"),
|
||||||
|
]),
|
||||||
|
content: r#"export const g = "g";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/g.d.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/g.d.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"export const g: "g";"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vec![(
|
||||||
|
"https://deno.land/x/example/a.ts",
|
||||||
|
CacheInfo {
|
||||||
|
local: Some(PathBuf::from(
|
||||||
|
"/cache/deps/https/deno.land/x/example/a.ts",
|
||||||
|
)),
|
||||||
|
emit: Some(PathBuf::from(
|
||||||
|
"/cache/deps/https/deno.land/x/example/a.js",
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
let root_specifier =
|
||||||
|
ModuleSpecifier::parse("https://deno.land/x/example/a.ts").unwrap();
|
||||||
|
let graph = deno_graph::create_graph(
|
||||||
|
vec![(root_specifier, ModuleKind::Esm)],
|
||||||
|
&mut loader,
|
||||||
|
deno_graph::GraphOptions {
|
||||||
|
is_dynamic: false,
|
||||||
|
imports: None,
|
||||||
|
resolver: None,
|
||||||
|
locker: None,
|
||||||
|
module_analyzer: None,
|
||||||
|
reporter: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut output = String::new();
|
||||||
|
fmt_module_graph(&graph, &mut output).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
strip_ansi_codes(&output),
|
||||||
|
r#"local: /cache/deps/https/deno.land/x/example/a.ts
|
||||||
|
emit: /cache/deps/https/deno.land/x/example/a.js
|
||||||
|
type: TypeScript
|
||||||
|
dependencies: 8 unique (total 477B)
|
||||||
|
|
||||||
|
https://deno.land/x/example/a.ts (129B)
|
||||||
|
├─┬ https://deno.land/x/example/b.ts (120B)
|
||||||
|
│ ├── https://deno.land/x/example/c.js (21B)
|
||||||
|
│ ├── https://deno.land/x/example/c.d.ts (20B)
|
||||||
|
│ └─┬ https://deno.land/x/example/d.ts (62B)
|
||||||
|
│ └─┬ https://deno.land/x/example/e.ts (62B)
|
||||||
|
│ └── https://deno.land/x/example/b.ts *
|
||||||
|
├── https://deno.land/x/example/f.d.ts (22B)
|
||||||
|
└─┬ https://deno.land/x/example/g.js (21B)
|
||||||
|
└── https://deno.land/x/example/g.d.ts (20B)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_info_graph_import_assertion() {
|
||||||
|
let mut loader = MemoryLoader::new(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/a.ts",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/a.ts",
|
||||||
|
maybe_headers: Some(vec![(
|
||||||
|
"content-type",
|
||||||
|
"application/typescript",
|
||||||
|
)]),
|
||||||
|
content: r#"import b from "./b.json" assert { type: "json" };
|
||||||
|
const c = await import("./c.json", { assert: { type: "json" } });
|
||||||
|
"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/b.json",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/b.json",
|
||||||
|
maybe_headers: Some(vec![("content-type", "application/json")]),
|
||||||
|
content: r#"{"b":"c"}"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/example/c.json",
|
||||||
|
Source::Module {
|
||||||
|
specifier: "https://deno.land/x/example/c.json",
|
||||||
|
maybe_headers: Some(vec![("content-type", "application/json")]),
|
||||||
|
content: r#"{"c":1}"#,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vec![(
|
||||||
|
"https://deno.land/x/example/a.ts",
|
||||||
|
CacheInfo {
|
||||||
|
local: Some(PathBuf::from(
|
||||||
|
"/cache/deps/https/deno.land/x/example/a.ts",
|
||||||
|
)),
|
||||||
|
emit: Some(PathBuf::from(
|
||||||
|
"/cache/deps/https/deno.land/x/example/a.js",
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
let root_specifier =
|
||||||
|
ModuleSpecifier::parse("https://deno.land/x/example/a.ts").unwrap();
|
||||||
|
let graph = deno_graph::create_graph(
|
||||||
|
vec![(root_specifier, ModuleKind::Esm)],
|
||||||
|
&mut loader,
|
||||||
|
deno_graph::GraphOptions {
|
||||||
|
is_dynamic: false,
|
||||||
|
imports: None,
|
||||||
|
resolver: None,
|
||||||
|
locker: None,
|
||||||
|
module_analyzer: None,
|
||||||
|
reporter: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut output = String::new();
|
||||||
|
fmt_module_graph(&graph, &mut output).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
strip_ansi_codes(&output),
|
||||||
|
r#"local: /cache/deps/https/deno.land/x/example/a.ts
|
||||||
|
emit: /cache/deps/https/deno.land/x/example/a.js
|
||||||
|
type: TypeScript
|
||||||
|
dependencies: 2 unique (total 156B)
|
||||||
|
|
||||||
|
https://deno.land/x/example/a.ts (140B)
|
||||||
|
├── https://deno.land/x/example/b.json (9B)
|
||||||
|
└── https://deno.land/x/example/c.json (7B)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ pub mod check;
|
||||||
pub mod coverage;
|
pub mod coverage;
|
||||||
pub mod doc;
|
pub mod doc;
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
|
pub mod info;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
pub mod installer;
|
pub mod installer;
|
||||||
pub mod lint;
|
pub mod lint;
|
||||||
|
|
Loading…
Add table
Reference in a new issue