1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

feat(cli): represent type dependencies in info (#9630)

Fixes #7927
This commit is contained in:
Kitson Kelly 2021-03-01 22:49:58 +11:00 committed by GitHub
parent 0dc89c0a79
commit 6dae627749
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 699 additions and 433 deletions

View file

@ -39,95 +39,107 @@ pub fn enable_ansi() {
BufferWriter::stdout(ColorChoice::AlwaysAnsi); BufferWriter::stdout(ColorChoice::AlwaysAnsi);
} }
fn style(s: &str, colorspec: ColorSpec) -> impl fmt::Display { fn style<S: AsRef<str>>(s: S, colorspec: ColorSpec) -> impl fmt::Display {
if !use_color() { if !use_color() {
return String::from(s); return String::from(s.as_ref());
} }
let mut v = Vec::new(); let mut v = Vec::new();
let mut ansi_writer = Ansi::new(&mut v); let mut ansi_writer = Ansi::new(&mut v);
ansi_writer.set_color(&colorspec).unwrap(); ansi_writer.set_color(&colorspec).unwrap();
ansi_writer.write_all(s.as_bytes()).unwrap(); ansi_writer.write_all(s.as_ref().as_bytes()).unwrap();
ansi_writer.reset().unwrap(); ansi_writer.reset().unwrap();
String::from_utf8_lossy(&v).into_owned() String::from_utf8_lossy(&v).into_owned()
} }
pub fn red_bold(s: &str) -> impl fmt::Display { pub fn red_bold<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Red)).set_bold(true); style_spec.set_fg(Some(Red)).set_bold(true);
style(&s, style_spec) style(s, style_spec)
} }
pub fn green_bold(s: &str) -> impl fmt::Display { pub fn green_bold<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Green)).set_bold(true); style_spec.set_fg(Some(Green)).set_bold(true);
style(&s, style_spec) style(s, style_spec)
} }
pub fn italic_bold(s: &str) -> impl fmt::Display { pub fn italic<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_italic(true);
style(s, style_spec)
}
pub fn italic_gray<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Ansi256(8))).set_italic(true);
style(s, style_spec)
}
pub fn italic_bold<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_bold(true).set_italic(true); style_spec.set_bold(true).set_italic(true);
style(&s, style_spec) style(s, style_spec)
} }
pub fn white_on_red(s: &str) -> impl fmt::Display { pub fn white_on_red<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_bg(Some(Red)).set_fg(Some(White)); style_spec.set_bg(Some(Red)).set_fg(Some(White));
style(&s, style_spec) style(s, style_spec)
} }
pub fn black_on_green(s: &str) -> impl fmt::Display { pub fn black_on_green<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_bg(Some(Green)).set_fg(Some(Black)); style_spec.set_bg(Some(Green)).set_fg(Some(Black));
style(&s, style_spec) style(s, style_spec)
} }
pub fn yellow(s: &str) -> impl fmt::Display { pub fn yellow<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Yellow)); style_spec.set_fg(Some(Yellow));
style(&s, style_spec) style(s, style_spec)
} }
pub fn cyan(s: &str) -> impl fmt::Display { pub fn cyan<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Cyan)); style_spec.set_fg(Some(Cyan));
style(&s, style_spec) style(s, style_spec)
} }
pub fn red(s: &str) -> impl fmt::Display { pub fn red<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Red)); style_spec.set_fg(Some(Red));
style(&s, style_spec) style(s, style_spec)
} }
pub fn green(s: &str) -> impl fmt::Display { pub fn green<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Green)); style_spec.set_fg(Some(Green));
style(&s, style_spec) style(s, style_spec)
} }
pub fn bold(s: &str) -> impl fmt::Display { pub fn bold<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_bold(true); style_spec.set_bold(true);
style(&s, style_spec) style(s, style_spec)
} }
pub fn gray(s: &str) -> impl fmt::Display { pub fn gray<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Ansi256(8))); style_spec.set_fg(Some(Ansi256(8)));
style(&s, style_spec) style(s, style_spec)
} }
pub fn italic_bold_gray(s: &str) -> impl fmt::Display { pub fn italic_bold_gray<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec style_spec
.set_fg(Some(Ansi256(8))) .set_fg(Some(Ansi256(8)))
.set_bold(true) .set_bold(true)
.set_italic(true); .set_italic(true);
style(&s, style_spec) style(s, style_spec)
} }
pub fn intense_blue(s: &str) -> impl fmt::Display { pub fn intense_blue<S: AsRef<str>>(s: S) -> impl fmt::Display {
let mut style_spec = ColorSpec::new(); let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Blue)).set_intense(true); style_spec.set_fg(Some(Blue)).set_intense(true);
style(&s, style_spec) style(s, style_spec)
} }

View file

@ -4,166 +4,239 @@ use crate::colors;
use crate::media_type::serialize_media_type; use crate::media_type::serialize_media_type;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use deno_core::resolve_url;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use std::cmp::Ordering; use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::iter::Iterator;
use std::path::PathBuf; use std::path::PathBuf;
/// The core structure representing information about a specific "root" file in const SIBLING_CONNECTOR: char = '├';
/// a module graph. This is used to represent information as part of the `info` const LAST_SIBLING_CONNECTOR: char = '└';
/// subcommand. const CHILD_DEPS_CONNECTOR: char = '┬';
#[derive(Debug, Serialize)] const CHILD_NO_DEPS_CONNECTOR: char = '─';
const VERTICAL_CONNECTOR: char = '│';
const EMPTY_CONNECTOR: char = ' ';
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ModuleGraphInfo { pub struct ModuleGraphInfoDep {
pub compiled: Option<PathBuf>, pub specifier: String,
pub dep_count: usize, pub is_dynamic: bool,
#[serde(serialize_with = "serialize_media_type")] #[serde(rename = "code", skip_serializing_if = "Option::is_none")]
pub file_type: MediaType, pub maybe_code: Option<ModuleSpecifier>,
pub files: ModuleInfoMap, #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing)] pub maybe_type: Option<ModuleSpecifier>,
pub info: ModuleInfo,
pub local: PathBuf,
pub map: Option<PathBuf>,
pub module: ModuleSpecifier,
pub total_size: usize,
} }
impl fmt::Display for ModuleGraphInfo { impl ModuleGraphInfoDep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn write_info<S: AsRef<str> + fmt::Display + Clone>(
writeln!(
f,
"{} {}",
colors::bold("local:"),
self.local.to_string_lossy()
)?;
writeln!(f, "{} {}", colors::bold("type:"), self.file_type)?;
if let Some(ref compiled) = self.compiled {
writeln!(
f,
"{} {}",
colors::bold("compiled:"),
compiled.to_string_lossy()
)?;
}
if let Some(ref map) = self.map {
writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
}
writeln!(
f,
"{} {} unique {}",
colors::bold("deps:"),
self.dep_count,
colors::gray(&format!(
"(total {})",
human_size(self.info.total_size.unwrap_or(0) as f64)
))
)?;
writeln!(f)?;
writeln!(
f,
"{} {}",
self.info.name,
colors::gray(&format!("({})", human_size(self.info.size as f64)))
)?;
let dep_count = self.info.deps.len();
for (idx, dep) in self.info.deps.iter().enumerate() {
dep.write_info(f, "", idx == dep_count - 1)?;
}
Ok(())
}
}
/// Represents a unique dependency within the graph of the the dependencies for
/// a given module.
#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ModuleInfo {
pub deps: Vec<ModuleInfo>,
pub name: ModuleSpecifier,
pub size: usize,
pub total_size: Option<usize>,
}
impl PartialOrd for ModuleInfo {
fn partial_cmp(&self, other: &ModuleInfo) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ModuleInfo {
fn cmp(&self, other: &ModuleInfo) -> Ordering {
self.name.to_string().cmp(&other.name.to_string())
}
}
impl ModuleInfo {
pub fn write_info(
&self, &self,
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter,
prefix: &str, prefix: S,
last: bool, last: bool,
modules: &[ModuleGraphInfoMod],
seen: &mut HashSet<ModuleSpecifier>,
) -> fmt::Result { ) -> fmt::Result {
let sibling_connector = if last { '└' } else { '├' }; let maybe_code = self
let child_connector = if self.deps.is_empty() { '─' } else { '┬' }; .maybe_code
let totals = if self.total_size.is_some() { .as_ref()
colors::gray(&format!(" ({})", human_size(self.size as f64))) .and_then(|s| modules.iter().find(|m| &m.specifier == s));
let maybe_type = self
.maybe_type
.as_ref()
.and_then(|s| modules.iter().find(|m| &m.specifier == s));
match (maybe_code, maybe_type) {
(Some(code), Some(types)) => {
code.write_info(f, prefix.clone(), false, false, modules, seen)?;
types.write_info(f, prefix, last, true, modules, seen)
}
(Some(code), None) => {
code.write_info(f, prefix, last, false, modules, seen)
}
(None, Some(types)) => {
types.write_info(f, prefix, last, true, modules, seen)
}
_ => Ok(()),
}
}
}
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ModuleGraphInfoMod {
pub specifier: ModuleSpecifier,
pub dependencies: Vec<ModuleGraphInfoDep>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<usize>,
#[serde(
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_media_type"
)]
pub media_type: Option<MediaType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub local: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub checksum: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub emit: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub map: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl Default for ModuleGraphInfoMod {
fn default() -> Self {
ModuleGraphInfoMod {
specifier: resolve_url("https://deno.land/x/mod.ts").unwrap(),
dependencies: Vec::new(),
size: None,
media_type: None,
local: None,
checksum: None,
emit: None,
map: None,
error: None,
}
}
}
impl ModuleGraphInfoMod {
fn write_info<S: AsRef<str> + fmt::Display>(
&self,
f: &mut fmt::Formatter,
prefix: S,
last: bool,
type_dep: bool,
modules: &[ModuleGraphInfoMod],
seen: &mut HashSet<ModuleSpecifier>,
) -> fmt::Result {
let was_seen = seen.contains(&self.specifier);
let sibling_connector = if last {
LAST_SIBLING_CONNECTOR
} else { } else {
colors::gray(" *") SIBLING_CONNECTOR
}; };
let child_connector = if self.dependencies.is_empty() || was_seen {
CHILD_NO_DEPS_CONNECTOR
} else {
CHILD_DEPS_CONNECTOR
};
let (size, specifier) = if self.error.is_some() {
(
colors::red_bold(" (error)").to_string(),
colors::red(&self.specifier).to_string(),
)
} else if was_seen {
let name = if type_dep {
colors::italic_gray(&self.specifier).to_string()
} else {
colors::gray(&self.specifier).to_string()
};
(colors::gray(" *").to_string(), name)
} else {
let name = if type_dep {
colors::italic(&self.specifier).to_string()
} else {
self.specifier.to_string()
};
(
colors::gray(format!(
" ({})",
human_size(self.size.unwrap_or(0) as f64)
))
.to_string(),
name,
)
};
seen.insert(self.specifier.clone());
writeln!( writeln!(
f, f,
"{} {}{}", "{} {}{}",
colors::gray(&format!( colors::gray(format!(
"{}{}─{}", "{}{}─{}",
prefix, sibling_connector, child_connector prefix, sibling_connector, child_connector
)), )),
self.name, specifier,
totals size
)?; )?;
let mut prefix = prefix.to_string(); if !was_seen {
if last { let mut prefix = prefix.to_string();
prefix.push(' '); if last {
} else { prefix.push(EMPTY_CONNECTOR);
prefix.push('│'); } else {
} prefix.push(VERTICAL_CONNECTOR);
prefix.push(' '); }
prefix.push(EMPTY_CONNECTOR);
let dep_count = self.deps.len(); let dep_count = self.dependencies.len();
for (idx, dep) in self.deps.iter().enumerate() { for (idx, dep) in self.dependencies.iter().enumerate() {
dep.write_info(f, &prefix, idx == dep_count - 1)?; dep.write_info(f, &prefix, idx == dep_count - 1, modules, seen)?;
}
} }
Ok(()) Ok(())
} }
} }
/// A flat map of dependencies for a given module graph. #[derive(Debug, Serialize)]
#[derive(Debug)] #[serde(rename_all = "camelCase")]
pub struct ModuleInfoMap(pub HashMap<ModuleSpecifier, ModuleInfoMapItem>); pub struct ModuleGraphInfo {
pub root: ModuleSpecifier,
impl ModuleInfoMap { pub modules: Vec<ModuleGraphInfoMod>,
pub fn new(map: HashMap<ModuleSpecifier, ModuleInfoMapItem>) -> Self { pub size: usize,
ModuleInfoMap(map)
}
} }
impl Serialize for ModuleInfoMap { impl fmt::Display for ModuleGraphInfo {
/// Serializes inner hash map which is ordered by the key fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> let root = self
where .modules
S: Serializer, .iter()
{ .find(|m| m.specifier == self.root)
let ordered: BTreeMap<_, _> = .unwrap();
self.0.iter().map(|(k, v)| (k.to_string(), v)).collect(); if let Some(err) = &root.error {
ordered.serialize(serializer) writeln!(f, "{} {}", colors::red("error:"), err)
} else {
if let Some(local) = &root.local {
writeln!(f, "{} {}", colors::bold("local:"), local.to_string_lossy())?;
}
if let Some(media_type) = &root.media_type {
writeln!(f, "{} {}", colors::bold("type:"), media_type)?;
}
if let Some(emit) = &root.emit {
writeln!(f, "{} {}", colors::bold("emit:"), emit.to_string_lossy())?;
}
if let Some(map) = &root.map {
writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
}
let dep_count = self.modules.len() - 1;
writeln!(
f,
"{} {} unique {}",
colors::bold("dependencies:"),
dep_count,
colors::gray(format!("(total {})", human_size(self.size as f64)))
)?;
writeln!(
f,
"\n{} {}",
self.root,
colors::gray(format!(
"({})",
human_size(root.size.unwrap_or(0) as f64)
))
)?;
let mut seen = HashSet::new();
let dep_len = root.dependencies.len();
for (idx, dep) in root.dependencies.iter().enumerate() {
dep.write_info(f, "", idx == dep_len - 1, &self.modules, &mut seen)?;
}
Ok(())
}
} }
} }
@ -202,7 +275,7 @@ pub fn human_size(size: f64) -> String {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use deno_core::resolve_url_or_path; use deno_core::resolve_url;
use deno_core::serde_json::json; use deno_core::serde_json::json;
#[test] #[test]
@ -219,58 +292,81 @@ mod test {
} }
fn get_fixture() -> ModuleGraphInfo { fn get_fixture() -> ModuleGraphInfo {
let spec_c = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); let specifier_a = resolve_url("https://deno.land/x/a.ts").unwrap();
let spec_d = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); let specifier_b = resolve_url("https://deno.land/x/b.ts").unwrap();
let deps = vec![ModuleInfo { let specifier_c_js = resolve_url("https://deno.land/x/c.js").unwrap();
deps: Vec::new(), let specifier_c_dts = resolve_url("https://deno.land/x/c.d.ts").unwrap();
name: spec_d.clone(), let modules = vec![
size: 12345, ModuleGraphInfoMod {
total_size: None, specifier: specifier_a.clone(),
}]; dependencies: vec![ModuleGraphInfoDep {
let info = ModuleInfo { specifier: "./b.ts".to_string(),
deps, is_dynamic: false,
name: spec_c.clone(), maybe_code: Some(specifier_b.clone()),
size: 12345, maybe_type: None,
total_size: Some(12345), }],
}; size: Some(123),
let mut items = HashMap::new(); media_type: Some(MediaType::TypeScript),
items.insert( local: Some(PathBuf::from("/cache/deps/https/deno.land/x/a.ts")),
spec_c, checksum: Some("abcdef".to_string()),
ModuleInfoMapItem { emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/a.js")),
deps: vec![spec_d.clone()], ..Default::default()
size: 12345,
}, },
); ModuleGraphInfoMod {
items.insert( specifier: specifier_b,
spec_d, dependencies: vec![ModuleGraphInfoDep {
ModuleInfoMapItem { specifier: "./c.js".to_string(),
deps: Vec::new(), is_dynamic: false,
size: 12345, maybe_code: Some(specifier_c_js.clone()),
maybe_type: Some(specifier_c_dts.clone()),
}],
size: Some(456),
media_type: Some(MediaType::TypeScript),
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/b.ts")),
checksum: Some("def123".to_string()),
emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/b.js")),
..Default::default()
}, },
); ModuleGraphInfoMod {
let files = ModuleInfoMap(items); specifier: specifier_c_js,
size: Some(789),
media_type: Some(MediaType::JavaScript),
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.js")),
checksum: Some("9876abcef".to_string()),
..Default::default()
},
ModuleGraphInfoMod {
specifier: specifier_c_dts,
size: Some(999),
media_type: Some(MediaType::Dts),
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.d.ts")),
checksum: Some("a2b3c4d5".to_string()),
..Default::default()
},
];
ModuleGraphInfo { ModuleGraphInfo {
compiled: Some(PathBuf::from("/a/b/c.js")), root: specifier_a,
dep_count: 99, modules,
file_type: MediaType::TypeScript, size: 99999,
files,
info,
local: PathBuf::from("/a/b/c.ts"),
map: None,
module: resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(),
total_size: 999999,
} }
} }
#[test] #[test]
fn test_module_graph_info_display() { fn text_module_graph_info_display() {
let fixture = get_fixture(); let fixture = get_fixture();
let actual = fixture.to_string(); let text = fixture.to_string();
assert!(actual.contains(" /a/b/c.ts")); let actual = colors::strip_ansi_codes(&text);
assert!(actual.contains(" 99 unique")); let expected = r#"local: /cache/deps/https/deno.land/x/a.ts
assert!(actual.contains("(12.06KB)")); type: TypeScript
assert!(actual.contains("\n\nhttps://deno.land/x/a/b/c.ts")); emit: /cache/emit/https/deno.land/x/a.js
dependencies: 3 unique (total 97.66KB)
https://deno.land/x/a.ts (123B)
https://deno.land/x/b.ts (456B)
https://deno.land/x/c.js (789B)
https://deno.land/x/c.d.ts (999B)
"#;
assert_eq!(actual, expected);
} }
#[test] #[test]
@ -280,19 +376,57 @@ mod test {
assert_eq!( assert_eq!(
actual, actual,
json!({ json!({
"compiled": "/a/b/c.js", "root": "https://deno.land/x/a.ts",
"depCount": 99, "modules": [
"fileType": "TypeScript", {
"files": { "specifier": "https://deno.land/x/a.ts",
"https://deno.land/x/a/b/c.ts":{ "dependencies": [
"deps": [], {
"size": 12345 "specifier": "./b.ts",
"isDynamic": false,
"code": "https://deno.land/x/b.ts"
}
],
"size": 123,
"mediaType": "TypeScript",
"local": "/cache/deps/https/deno.land/x/a.ts",
"checksum": "abcdef",
"emit": "/cache/emit/https/deno.land/x/a.js"
},
{
"specifier": "https://deno.land/x/b.ts",
"dependencies": [
{
"specifier": "./c.js",
"isDynamic": false,
"code": "https://deno.land/x/c.js",
"type": "https://deno.land/x/c.d.ts"
}
],
"size": 456,
"mediaType": "TypeScript",
"local": "/cache/deps/https/deno.land/x/b.ts",
"checksum": "def123",
"emit": "/cache/emit/https/deno.land/x/b.js"
},
{
"specifier": "https://deno.land/x/c.js",
"dependencies": [],
"size": 789,
"mediaType": "JavaScript",
"local": "/cache/deps/https/deno.land/x/c.js",
"checksum": "9876abcef"
},
{
"specifier": "https://deno.land/x/c.d.ts",
"dependencies": [],
"size": 999,
"mediaType": "Dts",
"local": "/cache/deps/https/deno.land/x/c.d.ts",
"checksum": "a2b3c4d5"
} }
}, ],
"local": "/a/b/c.ts", "size": 99999
"map": null,
"module": "https://deno.land/x/a/b/c.ts",
"totalSize": 999999
}) })
); );
} }

View file

@ -386,11 +386,11 @@ async fn info_command(
let info = graph.info()?; let info = graph.info()?;
if json { if json {
write_json_to_stdout(&json!(info))?; write_json_to_stdout(&json!(info))
} else { } else {
write_to_stdout_ignore_sigpipe(info.to_string().as_bytes())?; write_to_stdout_ignore_sigpipe(info.to_string().as_bytes())
.map_err(|err| err.into())
} }
Ok(())
} else { } else {
// If it was just "deno info" print location of caches and exit // If it was just "deno info" print location of caches and exit
print_cache_info(&program_state, json) print_cache_info(&program_state, json)

View file

@ -11,7 +11,7 @@ use std::path::PathBuf;
// Update carefully! // Update carefully!
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[repr(i32)] #[repr(i32)]
#[derive(Clone, Copy, Eq, PartialEq, Debug)] #[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Debug)]
pub enum MediaType { pub enum MediaType {
JavaScript = 0, JavaScript = 0,
JSX = 1, JSX = 1,
@ -184,11 +184,17 @@ impl Serialize for MediaType {
/// serialization for media types is and integer. /// serialization for media types is and integer.
/// ///
/// TODO(@kitsonk) remove this once we stop sending MediaType into tsc. /// TODO(@kitsonk) remove this once we stop sending MediaType into tsc.
pub fn serialize_media_type<S>(mt: &MediaType, s: S) -> Result<S::Ok, S::Error> pub fn serialize_media_type<S>(
mmt: &Option<MediaType>,
s: S,
) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
s.serialize_str(&mt.to_string()) match *mmt {
Some(ref mt) => s.serialize_some(&mt.to_string()),
None => s.serialize_none(),
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -6,13 +6,11 @@ use crate::ast::transpile_module;
use crate::ast::BundleHook; use crate::ast::BundleHook;
use crate::ast::Location; use crate::ast::Location;
use crate::ast::ParsedModule; use crate::ast::ParsedModule;
use crate::checksum;
use crate::colors; use crate::colors;
use crate::diagnostics::Diagnostics; use crate::diagnostics::Diagnostics;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::info::ModuleGraphInfo; use crate::info;
use crate::info::ModuleInfo;
use crate::info::ModuleInfoMap;
use crate::info::ModuleInfoMapItem;
use crate::lockfile::Lockfile; use crate::lockfile::Lockfile;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use crate::specifier_handler::CachedModule; use crate::specifier_handler::CachedModule;
@ -44,8 +42,8 @@ use deno_core::ModuleResolutionError;
use deno_core::ModuleSource; use deno_core::ModuleSource;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use regex::Regex; use regex::Regex;
use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::{BTreeSet, HashMap};
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
@ -1193,95 +1191,6 @@ impl Graph {
Ok(()) Ok(())
} }
fn get_info(
&self,
specifier: &ModuleSpecifier,
seen: &mut HashSet<ModuleSpecifier>,
totals: &mut HashMap<ModuleSpecifier, usize>,
) -> ModuleInfo {
let not_seen = seen.insert(specifier.clone());
let module = match self.get_module(specifier) {
ModuleSlot::Module(module) => module,
ModuleSlot::Err(err) => {
error!("{}: {}", colors::red_bold("error"), err.to_string());
std::process::exit(1);
}
_ => unreachable!(),
};
let mut deps = Vec::new();
let mut total_size = None;
if not_seen {
let mut seen_deps = HashSet::new();
// TODO(@kitsonk) https://github.com/denoland/deno/issues/7927
for (_, dep) in module.dependencies.iter() {
// Check the runtime code dependency
if let Some(code_dep) = &dep.maybe_code {
if seen_deps.insert(code_dep.clone()) {
deps.push(self.get_info(code_dep, seen, totals));
}
}
}
deps.sort();
total_size = if let Some(total) = totals.get(specifier) {
Some(total.to_owned())
} else {
let mut total = deps
.iter()
.map(|d| {
if let Some(total_size) = d.total_size {
total_size
} else {
0
}
})
.sum();
total += module.size();
totals.insert(specifier.clone(), total);
Some(total)
};
}
ModuleInfo {
deps,
name: specifier.clone(),
size: module.size(),
total_size,
}
}
fn get_info_map(&self) -> ModuleInfoMap {
let map = self
.modules
.iter()
.filter_map(|(specifier, module_slot)| {
if let ModuleSlot::Module(module) = module_slot {
let mut deps = BTreeSet::new();
for (_, dep) in module.dependencies.iter() {
if let Some(code_dep) = &dep.maybe_code {
deps.insert(code_dep.clone());
}
if let Some(type_dep) = &dep.maybe_type {
deps.insert(type_dep.clone());
}
}
if let Some((_, types_dep)) = &module.maybe_types {
deps.insert(types_dep.clone());
}
let item = ModuleInfoMapItem {
deps: deps.into_iter().collect(),
size: module.size(),
};
Some((specifier.clone(), item))
} else {
None
}
})
.collect();
ModuleInfoMap::new(map)
}
/// Retrieve a map that contains a representation of each module in the graph /// Retrieve a map that contains a representation of each module in the graph
/// which can be used to provide code to a module loader without holding all /// which can be used to provide code to a module loader without holding all
/// the state to be able to operate on the graph. /// the state to be able to operate on the graph.
@ -1425,51 +1334,75 @@ impl Graph {
/// Return a structure which provides information about the module graph and /// Return a structure which provides information about the module graph and
/// the relationship of the modules in the graph. This structure is used to /// the relationship of the modules in the graph. This structure is used to
/// provide information for the `info` subcommand. /// provide information for the `info` subcommand.
pub fn info(&self) -> Result<ModuleGraphInfo, AnyError> { pub fn info(&self) -> Result<info::ModuleGraphInfo, AnyError> {
if self.roots.is_empty() || self.roots.len() > 1 { if self.roots.is_empty() || self.roots.len() > 1 {
return Err(GraphError::NotSupported(format!("Info is only supported when there is a single root module in the graph. Found: {}", self.roots.len())).into()); return Err(GraphError::NotSupported(format!("Info is only supported when there is a single root module in the graph. Found: {}", self.roots.len())).into());
} }
let module = self.roots[0].clone(); let root = self.resolve_specifier(&self.roots[0]).clone();
let m = if let ModuleSlot::Module(module) = self.get_module(&module) { let mut modules: Vec<info::ModuleGraphInfoMod> = self
module
} else {
return Err(GraphError::MissingSpecifier(module.clone()).into());
};
let mut seen = HashSet::new();
let mut totals = HashMap::new();
let info = self.get_info(&module, &mut seen, &mut totals);
let files = self.get_info_map();
let total_size = totals.get(&module).unwrap_or(&m.size()).to_owned();
let (compiled, map) =
if let Some((emit_path, maybe_map_path)) = &m.maybe_emit_path {
(Some(emit_path.clone()), maybe_map_path.clone())
} else {
(None, None)
};
let dep_count = self
.modules .modules
.iter() .iter()
.filter_map(|(_, m)| match m { .filter_map(|(sp, sl)| match sl {
ModuleSlot::Module(_) => Some(1), ModuleSlot::Module(module) => {
let mut dependencies: Vec<info::ModuleGraphInfoDep> = module
.dependencies
.iter()
.map(|(k, v)| info::ModuleGraphInfoDep {
specifier: k.clone(),
is_dynamic: v.is_dynamic,
maybe_code: v
.maybe_code
.clone()
.map(|s| self.resolve_specifier(&s).clone()),
maybe_type: v
.maybe_type
.clone()
.map(|s| self.resolve_specifier(&s).clone()),
})
.collect();
dependencies.sort();
let (emit, map) =
if let Some((emit, maybe_map)) = &module.maybe_emit_path {
(Some(emit.clone()), maybe_map.clone())
} else {
(None, None)
};
Some(info::ModuleGraphInfoMod {
specifier: sp.clone(),
dependencies,
size: Some(module.size()),
media_type: Some(module.media_type),
local: Some(module.source_path.clone()),
checksum: Some(checksum::gen(&[module.source.as_bytes()])),
emit,
map,
..Default::default()
})
}
ModuleSlot::Err(err) => Some(info::ModuleGraphInfoMod {
specifier: sp.clone(),
error: Some(err.to_string()),
..Default::default()
}),
_ => None, _ => None,
}) })
.count() .collect();
- 1;
Ok(ModuleGraphInfo { modules.sort();
compiled,
dep_count, let size = modules.iter().fold(0_usize, |acc, m| {
file_type: m.media_type, if let Some(size) = &m.size {
files, acc + size
info, } else {
local: m.source_path.clone(), acc
map, }
module, });
total_size,
Ok(info::ModuleGraphInfo {
root,
modules,
size,
}) })
} }
@ -2514,19 +2447,11 @@ pub mod tests {
async fn test_graph_info() { async fn test_graph_info() {
let specifier = resolve_url_or_path("file:///tests/main.ts") let specifier = resolve_url_or_path("file:///tests/main.ts")
.expect("could not resolve module"); .expect("could not resolve module");
let (graph, _) = setup(specifier).await; let (graph, _) = setup(specifier.clone()).await;
let info = graph.info().expect("could not get info"); let info = graph.info().expect("could not get info");
assert!(info.compiled.is_none()); assert_eq!(info.root, specifier);
assert_eq!(info.dep_count, 6); assert_eq!(info.modules.len(), 7);
assert_eq!(info.file_type, MediaType::TypeScript); assert_eq!(info.size, 518);
assert_eq!(info.files.0.len(), 7);
assert!(info.local.to_string_lossy().ends_with("file_tests-main.ts"));
assert!(info.map.is_none());
assert_eq!(
info.module,
resolve_url_or_path("file:///tests/main.ts").unwrap()
);
assert_eq!(info.total_size, 344);
} }
#[tokio::test] #[tokio::test]

View file

@ -1,6 +1,6 @@
local: [WILDCARD]017_import_redirect.ts local: [WILDCARD]017_import_redirect.ts
type: TypeScript type: TypeScript
deps: 1 unique (total [WILDCARD]B) dependencies: 1 unique (total 278B)
file:///[WILDCARD]cli/tests/017_import_redirect.ts ([WILDCARD]) file:///[WILDCARD]cli/tests/017_import_redirect.ts ([WILDCARD])
└── http://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts ([WILDCARD]) └── https://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts ([WILDCARD])

View file

@ -1,7 +1,7 @@
[WILDCARD] [WILDCARD]
local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD]
type: TypeScript type: TypeScript
deps: 8 unique (total [WILDCARD]) dependencies: 8 unique (total [WILDCARD])
http://127.0.0.1:4545/cli/tests/019_media_types.ts ([WILDCARD]) http://127.0.0.1:4545/cli/tests/019_media_types.ts ([WILDCARD])
├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js ([WILDCARD]) ├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js ([WILDCARD])

View file

@ -1,5 +1,5 @@
[WILDCARD] [WILDCARD]
local: [WILDCARD]031_info_ts_error.ts local: [WILDCARD]031_info_ts_error.ts
type: TypeScript type: TypeScript
deps: 0 unique (total [WILDCARD]) dependencies: 0 unique (total [WILDCARD])
[WILDCARD]031_info_ts_error.ts ([WILDCARD]) [WILDCARD]031_info_ts_error.ts ([WILDCARD])

View file

@ -1,7 +1,7 @@
[WILDCARD] [WILDCARD]
local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD]
type: TypeScript type: TypeScript
deps: 8 unique (total [WILDCARD]) dependencies: 8 unique (total [WILDCARD])
http://127.0.0.1:4545/cli/tests/048_media_types_jsx.ts ([WILDCARD]) http://127.0.0.1:4545/cli/tests/048_media_types_jsx.ts ([WILDCARD])
├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript_jsx.j2.jsx ([WILDCARD]) ├── http://localhost:4545/cli/tests/subdir/mt_application_ecmascript_jsx.j2.jsx ([WILDCARD])

View file

@ -1,6 +1,6 @@
local: [WILDCARD]005_more_imports.ts local: [WILDCARD]005_more_imports.ts
type: TypeScript type: TypeScript
deps: 3 unique (total [WILDCARD]) dependencies: 3 unique (total [WILDCARD])
file://[WILDCARD]/005_more_imports.ts ([WILDCARD]) file://[WILDCARD]/005_more_imports.ts ([WILDCARD])
└─┬ file://[WILDCARD]/subdir/mod1.ts ([WILDCARD]) └─┬ file://[WILDCARD]/subdir/mod1.ts ([WILDCARD])

View file

@ -1,33 +1,56 @@
{ {
"compiled": null, "root": "file://[WILDCARD]/cli/tests/005_more_imports.ts",
"depCount": 3, "modules": [
"fileType": "TypeScript", {
"files": { "specifier": "file://[WILDCARD]/cli/tests/005_more_imports.ts",
"file:///[WILDCARD]/cli/tests/005_more_imports.ts": { "dependencies": [
"deps": [ {
"file:///[WILDCARD]/cli/tests/subdir/mod1.ts" "specifier": "./subdir/mod1.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/subdir/mod1.ts"
}
], ],
"size": 211 "size": 211,
"mediaType": "TypeScript",
"local": "[WILDCARD]005_more_imports.ts",
[WILDCARD]
}, },
"file:///[WILDCARD]/cli/tests/subdir/mod1.ts": { {
"deps": [ "specifier": "file://[WILDCARD]/cli/tests/subdir/mod1.ts",
"file:///[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts" "dependencies": [
{
"specifier": "./subdir2/mod2.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts"
}
], ],
"size": 320 "size": 320,
"mediaType": "TypeScript",
"local": "[WILDCARD]mod1.ts",
[WILDCARD]
}, },
"file:///[WILDCARD]/cli/tests/subdir/print_hello.ts": { {
"deps": [], "specifier": "file://[WILDCARD]/cli/tests/subdir/print_hello.ts",
"size": 63 "dependencies": [],
"size": 63,
"mediaType": "TypeScript",
"local": "[WILDCARD]print_hello.ts",
[WILDCARD]
}, },
"file:///[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts": { {
"deps": [ "specifier": "file://[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts",
"file:///[WILDCARD]/cli/tests/subdir/print_hello.ts" "dependencies": [
{
"specifier": "../print_hello.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/subdir/print_hello.ts"
}
], ],
"size": 163 "size": 163,
"mediaType": "TypeScript",
"local": "[WILDCARD]mod2.ts",
[WILDCARD]
} }
}, ],
"local": "[WILDCARD]005_more_imports.ts", "size": 757
"map": null,
"module": "file:///[WILDCARD]/cli/tests/005_more_imports.ts",
"totalSize": 757
} }

View file

@ -1,5 +1,5 @@
[WILDCARD] [WILDCARD]
local: [WILDCARD]test.ts local: [WILDCARD]test.ts
type: TypeScript type: TypeScript
deps: 7 unique (total [WILDCARD]) dependencies: 7 unique (total [WILDCARD])
[WILDCARD] [WILDCARD]

View file

@ -1,38 +1,85 @@
{ {
"compiled": null, "root": "file://[WILDCARD]/cli/tests/076_info_json_deps_order.ts",
"depCount": 4, "modules": [
"fileType": "TypeScript", {
"files": { "specifier": "file://[WILDCARD]/cli/tests/076_info_json_deps_order.ts",
"[WILDCARD]cli/tests/076_info_json_deps_order.ts": { "dependencies": [
"deps": [ {
"[WILDCARD]cli/tests/recursive_imports/A.ts" "specifier": "./recursive_imports/A.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts"
}
], ],
"size": [WILDCARD] "size": 46,
"mediaType": "TypeScript",
"local": "[WILDCARD]076_info_json_deps_order.ts",
"checksum": "88b144f362d31ac42263648aadef727dd36d039d3b8ac0248fdaff25d4de415a"
}, },
"[WILDCARD]cli/tests/recursive_imports/A.ts": { {
"deps": [ "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts",
"[WILDCARD]cli/tests/recursive_imports/B.ts", "dependencies": [
"[WILDCARD]cli/tests/recursive_imports/common.ts" {
"specifier": "./B.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/B.ts"
},
{
"specifier": "./common.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/common.ts"
}
], ],
"size": [WILDCARD] "size": 114,
"mediaType": "TypeScript",
"local": "[WILDCARD]A.ts",
"checksum": "da204c16d3114763810864083af8891a887d65fbe34e4c8b5bf985dbc8f0b01f"
}, },
"[WILDCARD]cli/tests/recursive_imports/B.ts": { {
"deps": [ "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/B.ts",
"[WILDCARD]cli/tests/recursive_imports/C.ts", "dependencies": [
"[WILDCARD]cli/tests/recursive_imports/common.ts" {
"specifier": "./C.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/C.ts"
},
{
"specifier": "./common.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/common.ts"
}
], ],
"size": [WILDCARD] "size": 114,
"mediaType": "TypeScript",
"local": "[WILDCARD]B.ts",
"checksum": "060ef62435d7e3a3276e8894307b19cf17772210a20dd091d24a670fadec6b83"
}, },
"[WILDCARD]cli/tests/recursive_imports/C.ts": { {
"deps": [ "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/C.ts",
"[WILDCARD]cli/tests/recursive_imports/A.ts", "dependencies": [
"[WILDCARD]cli/tests/recursive_imports/common.ts" {
"specifier": "./A.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts"
},
{
"specifier": "./common.ts",
"isDynamic": false,
"code": "file://[WILDCARD]/cli/tests/recursive_imports/common.ts"
}
], ],
"size": [WILDCARD] "size": 132,
"mediaType": "TypeScript",
"local": "[WILDCARD]C.ts",
"checksum": "5190563583617a69f190f1cc76e6552df878df278cfaa5d5e30ebe0938cf5e0b"
}, },
"[WILDCARD]cli/tests/recursive_imports/common.ts": { {
"deps": [], "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/common.ts",
"size": [WILDCARD] "dependencies": [],
"size": 34,
"mediaType": "TypeScript",
"local": "[WILDCARD]common.ts",
"checksum": "01b595d69514bfd001ba2cf421feabeaef559513f10697bf1a22781f8a8ed7f0"
} }
}, ],
[WILDCARD] "size": 440
}

View file

@ -1,2 +1,6 @@
error: Cannot resolve module "file://[WILDCARD]/bad-module.js" from "file://[WILDCARD]/error_009_missing_js_module.js". local: [WILDCARD]error_009_missing_js_module.js
at file://[WILDCARD]/error_009_missing_js_module.js:1:0 type: JavaScript
dependencies: 1 unique (total 26B)
file://[WILDCARD]/cli/tests/error_009_missing_js_module.js (26B)
└── file://[WILDCARD]/cli/tests/bad-module.js (error)

View file

@ -1,12 +1,12 @@
local: [WILDCARD]info_recursive_imports_test.ts local: [WILDCARD]info_recursive_imports_test.ts
type: TypeScript type: TypeScript
deps: 4 unique (total [WILDCARD]) dependencies: 4 unique (total [WILDCARD])
file://[WILDCARD]cli/tests/info_recursive_imports_test.ts ([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/A.ts ([WILDCARD])
├─┬ file://[WILDCARD]cli/tests/recursive_imports/B.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/C.ts ([WILDCARD])
│ │ ├── file://[WILDCARD]cli/tests/recursive_imports/A.ts * │ │ ├── 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 ([WILDCARD])
│ └── 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 [WILDCARD] └── file://[WILDCARD]cli/tests/recursive_imports/common.ts *

View file

@ -1,5 +1,5 @@
local: [WILDCARD]info_type_import.ts local: [WILDCARD]info_type_import.ts
type: TypeScript type: TypeScript
deps: 1 unique (total [WILDCARD]) dependencies: 1 unique (total [WILDCARD])
[WILDCARD]info_type_import.ts ([WILDCARD]) [WILDCARD]info_type_import.ts ([WILDCARD])
└── [WILDCARD]type_and_code.ts ([WILDCARD]) └── [WILDCARD]type_and_code.ts ([WILDCARD])

View file

@ -1732,7 +1732,7 @@ mod integration {
let str_output = std::str::from_utf8(&output.stdout).unwrap().trim(); let str_output = std::str::from_utf8(&output.stdout).unwrap().trim();
eprintln!("{}", str_output); eprintln!("{}", str_output);
// check the output of the test.ts program. // check the output of the test.ts program.
assert!(str_output.contains("compiled: ")); assert!(str_output.contains("emit: "));
assert_eq!(output.stderr, b""); assert_eq!(output.stderr, b"");
} }
@ -3720,7 +3720,6 @@ console.log("finish");
itest!(info_missing_module { itest!(info_missing_module {
args: "info error_009_missing_js_module.js", args: "info error_009_missing_js_module.js",
output: "info_missing_module.out", output: "info_missing_module.out",
exit_code: 1,
}); });
itest!(info_recursive_modules { itest!(info_recursive_modules {

View file

@ -521,7 +521,7 @@ impl CoverageReporter for PrettyCoverageReporter {
// Put a horizontal separator between disjoint runs of lines // Put a horizontal separator between disjoint runs of lines
if let Some(last_line) = last_line { if let Some(last_line) = last_line {
if last_line + 1 != line_index { if last_line + 1 != line_index {
let dash = colors::gray(&"-".repeat(WIDTH + 1)); let dash = colors::gray("-".repeat(WIDTH + 1));
println!("{}{}{}", dash, colors::gray(SEPERATOR), dash); println!("{}{}{}", dash, colors::gray(SEPERATOR), dash);
} }
} }

View file

@ -0,0 +1,116 @@
{
"$id": "https://deno.land/schemas/module-graph.json",
"$schema": "http://json-schema.org/draft-07/schema",
"description": "A JSON representation of a Deno module dependency graph.",
"required": [
"root",
"modules",
"size"
],
"title": "Deno Dependency Graph Schema",
"type": "object",
"properties": {
"root": {
"default": "",
"description": "The root specifier for the graph.",
"examples": [
"https://deno.land/x/mod.ts"
],
"type": "string"
},
"modules": {
"default": [],
"description": "The modules that are part of the graph.",
"type": "array",
"items": {
"$ref": "#/definitions/module"
}
},
"size": {
"type": "integer",
"description": "The total size of all the unique dependencies in the graph in bytes.",
"default": 0
}
},
"definitions": {
"module": {
"type": "object",
"required": [
"specifier"
],
"properties": {
"specifier": {
"type": "string",
"description": "The fully qualified module specifier (URL) for the module."
},
"dependencies": {
"type": "array",
"description": "An array of dependencies of the module.",
"items": {
"$ref": "#/definitions/dependency"
}
},
"size": {
"type": "integer",
"description": "The size of the module on disk in bytes."
},
"mediaType": {
"type": "string",
"description": "How the file is treated within Deno. All the possible media types that Deno considers are listed here, but in practice, several of them would never appear in a module graph.",
"enum": [
"JavaScript",
"TypeScript",
"JSX",
"TSX",
"Dts",
"Json",
"Wasm",
"TsBuildInfo",
"SourceMap",
"Unknown"
]
},
"local": {
"type": "string",
"description": "The path to the local file. For local modules this will be the local file path, for remote modules and data URLs, this would be the path to the file in the Deno cache."
},
"checksum": {
"type": "string",
"description": "The checksum of the local source file. This can be used to validate if the current on disk version matches the version described here."
},
"emit": {
"type": "string",
"description": "The path to an emitted version of the module, if the module requires transpilation to be loaded into the Deno runtime."
},
"map": {
"type": "string",
"description": "The path to an optionally emitted source map between the original and emitted version of the file."
},
"error": {
"type": "string",
"description": "If when resolving the module, Deno encountered an error and the module is unavailable, the text of that error will be indicated here."
}
}
},
"dependency": {
"type": "object",
"required": [
"specifier"
],
"properties": {
"specifier": {
"type": "string",
"description": "The specifier provided from within the module."
},
"code": {
"type": "string",
"description": "The fully qualified module specifier (URL) for the code dependency."
},
"type": {
"type": "string",
"description": "The fully qualified module specifier (URL) for the type only dependency."
}
}
}
}
}