diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/colors.rs | 70 | ||||
-rw-r--r-- | cli/info.rs | 488 | ||||
-rw-r--r-- | cli/main.rs | 6 | ||||
-rw-r--r-- | cli/media_type.rs | 12 | ||||
-rw-r--r-- | cli/module_graph.rs | 211 | ||||
-rw-r--r-- | cli/tests/017_import_redirect_info.out | 4 | ||||
-rw-r--r-- | cli/tests/022_info_flag_script.out | 2 | ||||
-rw-r--r-- | cli/tests/031_info_ts_error.out | 2 | ||||
-rw-r--r-- | cli/tests/049_info_flag_script_jsx.out | 2 | ||||
-rw-r--r-- | cli/tests/054_info_local_imports.out | 2 | ||||
-rw-r--r-- | cli/tests/055_info_file_json.out | 71 | ||||
-rw-r--r-- | cli/tests/065_import_map_info.out | 2 | ||||
-rw-r--r-- | cli/tests/076_info_json_deps_order.out | 103 | ||||
-rw-r--r-- | cli/tests/info_missing_module.out | 8 | ||||
-rw-r--r-- | cli/tests/info_recursive_imports_test.out | 8 | ||||
-rw-r--r-- | cli/tests/info_type_import.out | 2 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 3 | ||||
-rw-r--r-- | cli/tools/coverage.rs | 2 |
18 files changed, 574 insertions, 424 deletions
diff --git a/cli/colors.rs b/cli/colors.rs index 847f8c765..278c141b4 100644 --- a/cli/colors.rs +++ b/cli/colors.rs @@ -39,95 +39,107 @@ pub fn enable_ansi() { 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() { - return String::from(s); + return String::from(s.as_ref()); } let mut v = Vec::new(); let mut ansi_writer = Ansi::new(&mut v); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); style_spec .set_fg(Some(Ansi256(8))) .set_bold(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(); style_spec.set_fg(Some(Blue)).set_intense(true); - style(&s, style_spec) + style(s, style_spec) } diff --git a/cli/info.rs b/cli/info.rs index 62ca9a7f9..d80e58481 100644 --- a/cli/info.rs +++ b/cli/info.rs @@ -4,166 +4,239 @@ use crate::colors; use crate::media_type::serialize_media_type; use crate::media_type::MediaType; +use deno_core::resolve_url; use deno_core::serde::Serialize; -use deno_core::serde::Serializer; use deno_core::ModuleSpecifier; -use std::cmp::Ordering; -use std::collections::BTreeMap; -use std::collections::HashMap; +use std::collections::HashSet; use std::fmt; +use std::iter::Iterator; use std::path::PathBuf; -/// The core structure representing information about a specific "root" file in -/// a module graph. This is used to represent information as part of the `info` -/// subcommand. -#[derive(Debug, Serialize)] +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 = ' '; + +#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)] #[serde(rename_all = "camelCase")] -pub struct ModuleGraphInfo { - pub compiled: Option<PathBuf>, - pub dep_count: usize, - #[serde(serialize_with = "serialize_media_type")] - pub file_type: MediaType, - pub files: ModuleInfoMap, - #[serde(skip_serializing)] - pub info: ModuleInfo, - pub local: PathBuf, - pub map: Option<PathBuf>, - pub module: ModuleSpecifier, - pub total_size: usize, +pub struct ModuleGraphInfoDep { + pub specifier: String, + pub is_dynamic: bool, + #[serde(rename = "code", skip_serializing_if = "Option::is_none")] + pub maybe_code: Option<ModuleSpecifier>, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub maybe_type: Option<ModuleSpecifier>, } -impl fmt::Display for ModuleGraphInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - 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)?; +impl ModuleGraphInfoDep { + fn write_info<S: AsRef<str> + fmt::Display + Clone>( + &self, + f: &mut fmt::Formatter, + prefix: S, + last: bool, + modules: &[ModuleGraphInfoMod], + seen: &mut HashSet<ModuleSpecifier>, + ) -> fmt::Result { + let maybe_code = self + .maybe_code + .as_ref() + .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(()), } - - Ok(()) } } -/// Represents a unique dependency within the graph of the the dependencies for -/// a given module. -#[derive(Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize, Ord, PartialOrd, 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)) - } +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 Ord for ModuleInfo { - fn cmp(&self, other: &ModuleInfo) -> Ordering { - self.name.to_string().cmp(&other.name.to_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 ModuleInfo { - pub fn write_info( +impl ModuleGraphInfoMod { + fn write_info<S: AsRef<str> + fmt::Display>( &self, - f: &mut fmt::Formatter<'_>, - prefix: &str, + f: &mut fmt::Formatter, + prefix: S, last: bool, + type_dep: bool, + modules: &[ModuleGraphInfoMod], + seen: &mut HashSet<ModuleSpecifier>, ) -> fmt::Result { - let sibling_connector = if last { '└' } else { '├' }; - let child_connector = if self.deps.is_empty() { '─' } else { '┬' }; - let totals = if self.total_size.is_some() { - colors::gray(&format!(" ({})", human_size(self.size as f64))) + let was_seen = seen.contains(&self.specifier); + let sibling_connector = if last { + LAST_SIBLING_CONNECTOR } 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!( f, "{} {}{}", - colors::gray(&format!( + colors::gray(format!( "{}{}─{}", prefix, sibling_connector, child_connector )), - self.name, - totals + specifier, + size )?; - let mut prefix = prefix.to_string(); - if last { - prefix.push(' '); - } else { - prefix.push('│'); - } - prefix.push(' '); - - let dep_count = self.deps.len(); - for (idx, dep) in self.deps.iter().enumerate() { - dep.write_info(f, &prefix, idx == dep_count - 1)?; + 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_count = self.dependencies.len(); + for (idx, dep) in self.dependencies.iter().enumerate() { + dep.write_info(f, &prefix, idx == dep_count - 1, modules, seen)?; + } } Ok(()) } } -/// A flat map of dependencies for a given module graph. -#[derive(Debug)] -pub struct ModuleInfoMap(pub HashMap<ModuleSpecifier, ModuleInfoMapItem>); - -impl ModuleInfoMap { - pub fn new(map: HashMap<ModuleSpecifier, ModuleInfoMapItem>) -> Self { - ModuleInfoMap(map) - } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ModuleGraphInfo { + pub root: ModuleSpecifier, + pub modules: Vec<ModuleGraphInfoMod>, + pub size: usize, } -impl Serialize for ModuleInfoMap { - /// Serializes inner hash map which is ordered by the key - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let ordered: BTreeMap<_, _> = - self.0.iter().map(|(k, v)| (k.to_string(), v)).collect(); - ordered.serialize(serializer) +impl fmt::Display for ModuleGraphInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let root = self + .modules + .iter() + .find(|m| m.specifier == self.root) + .unwrap(); + if let Some(err) = &root.error { + 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)] mod test { use super::*; - use deno_core::resolve_url_or_path; + use deno_core::resolve_url; use deno_core::serde_json::json; #[test] @@ -219,58 +292,81 @@ mod test { } fn get_fixture() -> ModuleGraphInfo { - let spec_c = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); - let spec_d = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); - let deps = vec![ModuleInfo { - deps: Vec::new(), - name: spec_d.clone(), - size: 12345, - total_size: None, - }]; - let info = ModuleInfo { - deps, - name: spec_c.clone(), - size: 12345, - total_size: Some(12345), - }; - let mut items = HashMap::new(); - items.insert( - spec_c, - ModuleInfoMapItem { - deps: vec![spec_d.clone()], - size: 12345, + let specifier_a = resolve_url("https://deno.land/x/a.ts").unwrap(); + let specifier_b = resolve_url("https://deno.land/x/b.ts").unwrap(); + let specifier_c_js = resolve_url("https://deno.land/x/c.js").unwrap(); + let specifier_c_dts = resolve_url("https://deno.land/x/c.d.ts").unwrap(); + let modules = vec![ + ModuleGraphInfoMod { + specifier: specifier_a.clone(), + dependencies: vec![ModuleGraphInfoDep { + specifier: "./b.ts".to_string(), + is_dynamic: false, + maybe_code: Some(specifier_b.clone()), + maybe_type: None, + }], + size: Some(123), + media_type: Some(MediaType::TypeScript), + local: Some(PathBuf::from("/cache/deps/https/deno.land/x/a.ts")), + checksum: Some("abcdef".to_string()), + emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/a.js")), + ..Default::default() }, - ); - items.insert( - spec_d, - ModuleInfoMapItem { - deps: Vec::new(), - size: 12345, + ModuleGraphInfoMod { + specifier: specifier_b, + dependencies: vec![ModuleGraphInfoDep { + specifier: "./c.js".to_string(), + is_dynamic: false, + 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() }, - ); - let files = ModuleInfoMap(items); - + ModuleGraphInfoMod { + 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 { - compiled: Some(PathBuf::from("/a/b/c.js")), - dep_count: 99, - file_type: MediaType::TypeScript, - 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, + root: specifier_a, + modules, + size: 99999, } } #[test] - fn test_module_graph_info_display() { + fn text_module_graph_info_display() { let fixture = get_fixture(); - let actual = fixture.to_string(); - assert!(actual.contains(" /a/b/c.ts")); - assert!(actual.contains(" 99 unique")); - assert!(actual.contains("(12.06KB)")); - assert!(actual.contains("\n\nhttps://deno.land/x/a/b/c.ts")); + let text = fixture.to_string(); + let actual = colors::strip_ansi_codes(&text); + let expected = r#"local: /cache/deps/https/deno.land/x/a.ts +type: TypeScript +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] @@ -280,19 +376,57 @@ mod test { assert_eq!( actual, json!({ - "compiled": "/a/b/c.js", - "depCount": 99, - "fileType": "TypeScript", - "files": { - "https://deno.land/x/a/b/c.ts":{ - "deps": [], - "size": 12345 + "root": "https://deno.land/x/a.ts", + "modules": [ + { + "specifier": "https://deno.land/x/a.ts", + "dependencies": [ + { + "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", - "map": null, - "module": "https://deno.land/x/a/b/c.ts", - "totalSize": 999999 + ], + "size": 99999 }) ); } diff --git a/cli/main.rs b/cli/main.rs index 0fa63a138..2b958ff55 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -386,11 +386,11 @@ async fn info_command( let info = graph.info()?; if json { - write_json_to_stdout(&json!(info))?; + write_json_to_stdout(&json!(info)) } 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 { // If it was just "deno info" print location of caches and exit print_cache_info(&program_state, json) diff --git a/cli/media_type.rs b/cli/media_type.rs index 3d5c914f3..922784902 100644 --- a/cli/media_type.rs +++ b/cli/media_type.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; // Update carefully! #[allow(non_camel_case_types)] #[repr(i32)] -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Debug)] pub enum MediaType { JavaScript = 0, JSX = 1, @@ -184,11 +184,17 @@ impl Serialize for MediaType { /// serialization for media types is and integer. /// /// 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 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)] diff --git a/cli/module_graph.rs b/cli/module_graph.rs index dc4495157..8a3c140ba 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -6,13 +6,11 @@ use crate::ast::transpile_module; use crate::ast::BundleHook; use crate::ast::Location; use crate::ast::ParsedModule; +use crate::checksum; use crate::colors; use crate::diagnostics::Diagnostics; use crate::import_map::ImportMap; -use crate::info::ModuleGraphInfo; -use crate::info::ModuleInfo; -use crate::info::ModuleInfoMap; -use crate::info::ModuleInfoMapItem; +use crate::info; use crate::lockfile::Lockfile; use crate::media_type::MediaType; use crate::specifier_handler::CachedModule; @@ -44,8 +42,8 @@ use deno_core::ModuleResolutionError; use deno_core::ModuleSource; use deno_core::ModuleSpecifier; use regex::Regex; +use std::collections::HashMap; use std::collections::HashSet; -use std::collections::{BTreeSet, HashMap}; use std::error::Error; use std::fmt; use std::path::PathBuf; @@ -1193,95 +1191,6 @@ impl Graph { 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 /// which can be used to provide code to a module loader without holding all /// 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 /// the relationship of the modules in the graph. This structure is used to /// 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 { 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 m = if let ModuleSlot::Module(module) = self.get_module(&module) { - 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 + let root = self.resolve_specifier(&self.roots[0]).clone(); + let mut modules: Vec<info::ModuleGraphInfoMod> = self .modules .iter() - .filter_map(|(_, m)| match m { - ModuleSlot::Module(_) => Some(1), + .filter_map(|(sp, sl)| match sl { + 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, }) - .count() - - 1; - - Ok(ModuleGraphInfo { - compiled, - dep_count, - file_type: m.media_type, - files, - info, - local: m.source_path.clone(), - map, - module, - total_size, + .collect(); + + modules.sort(); + + let size = modules.iter().fold(0_usize, |acc, m| { + if let Some(size) = &m.size { + acc + size + } else { + acc + } + }); + + Ok(info::ModuleGraphInfo { + root, + modules, + size, }) } @@ -2514,19 +2447,11 @@ pub mod tests { async fn test_graph_info() { let specifier = resolve_url_or_path("file:///tests/main.ts") .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"); - assert!(info.compiled.is_none()); - assert_eq!(info.dep_count, 6); - assert_eq!(info.file_type, MediaType::TypeScript); - 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); + assert_eq!(info.root, specifier); + assert_eq!(info.modules.len(), 7); + assert_eq!(info.size, 518); } #[tokio::test] diff --git a/cli/tests/017_import_redirect_info.out b/cli/tests/017_import_redirect_info.out index ff23915a9..662b237a4 100644 --- a/cli/tests/017_import_redirect_info.out +++ b/cli/tests/017_import_redirect_info.out @@ -1,6 +1,6 @@ local: [WILDCARD]017_import_redirect.ts type: TypeScript -deps: 1 unique (total [WILDCARD]B) +dependencies: 1 unique (total 278B) 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]) diff --git a/cli/tests/022_info_flag_script.out b/cli/tests/022_info_flag_script.out index 83425f09f..878b0fa08 100644 --- a/cli/tests/022_info_flag_script.out +++ b/cli/tests/022_info_flag_script.out @@ -1,7 +1,7 @@ [WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] 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://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js ([WILDCARD]) diff --git a/cli/tests/031_info_ts_error.out b/cli/tests/031_info_ts_error.out index 8c3064cdf..2dc7bb4e8 100644 --- a/cli/tests/031_info_ts_error.out +++ b/cli/tests/031_info_ts_error.out @@ -1,5 +1,5 @@ [WILDCARD] local: [WILDCARD]031_info_ts_error.ts type: TypeScript -deps: 0 unique (total [WILDCARD]) +dependencies: 0 unique (total [WILDCARD]) [WILDCARD]031_info_ts_error.ts ([WILDCARD]) diff --git a/cli/tests/049_info_flag_script_jsx.out b/cli/tests/049_info_flag_script_jsx.out index 9260e8f67..0ad6608d4 100644 --- a/cli/tests/049_info_flag_script_jsx.out +++ b/cli/tests/049_info_flag_script_jsx.out @@ -1,7 +1,7 @@ [WILDCARD] local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD] 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://localhost:4545/cli/tests/subdir/mt_application_ecmascript_jsx.j2.jsx ([WILDCARD]) diff --git a/cli/tests/054_info_local_imports.out b/cli/tests/054_info_local_imports.out index 1933a0c9c..cde5ff3ab 100644 --- a/cli/tests/054_info_local_imports.out +++ b/cli/tests/054_info_local_imports.out @@ -1,6 +1,6 @@ local: [WILDCARD]005_more_imports.ts type: TypeScript -deps: 3 unique (total [WILDCARD]) +dependencies: 3 unique (total [WILDCARD]) file://[WILDCARD]/005_more_imports.ts ([WILDCARD]) └─┬ file://[WILDCARD]/subdir/mod1.ts ([WILDCARD]) diff --git a/cli/tests/055_info_file_json.out b/cli/tests/055_info_file_json.out index d0274b0b8..602c48b32 100644 --- a/cli/tests/055_info_file_json.out +++ b/cli/tests/055_info_file_json.out @@ -1,33 +1,56 @@ { - "compiled": null, - "depCount": 3, - "fileType": "TypeScript", - "files": { - "file:///[WILDCARD]/cli/tests/005_more_imports.ts": { - "deps": [ - "file:///[WILDCARD]/cli/tests/subdir/mod1.ts" + "root": "file://[WILDCARD]/cli/tests/005_more_imports.ts", + "modules": [ + { + "specifier": "file://[WILDCARD]/cli/tests/005_more_imports.ts", + "dependencies": [ + { + "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": [ - "file:///[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts" + { + "specifier": "file://[WILDCARD]/cli/tests/subdir/mod1.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": [], - "size": 63 + { + "specifier": "file://[WILDCARD]/cli/tests/subdir/print_hello.ts", + "dependencies": [], + "size": 63, + "mediaType": "TypeScript", + "local": "[WILDCARD]print_hello.ts", + [WILDCARD] }, - "file:///[WILDCARD]/cli/tests/subdir/subdir2/mod2.ts": { - "deps": [ - "file:///[WILDCARD]/cli/tests/subdir/print_hello.ts" + { + "specifier": "file://[WILDCARD]/cli/tests/subdir/subdir2/mod2.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", - "map": null, - "module": "file:///[WILDCARD]/cli/tests/005_more_imports.ts", - "totalSize": 757 + ], + "size": 757 }
\ No newline at end of file diff --git a/cli/tests/065_import_map_info.out b/cli/tests/065_import_map_info.out index 5407b7d80..8771d0594 100644 --- a/cli/tests/065_import_map_info.out +++ b/cli/tests/065_import_map_info.out @@ -1,5 +1,5 @@ [WILDCARD] local: [WILDCARD]test.ts type: TypeScript -deps: 7 unique (total [WILDCARD]) +dependencies: 7 unique (total [WILDCARD]) [WILDCARD] diff --git a/cli/tests/076_info_json_deps_order.out b/cli/tests/076_info_json_deps_order.out index 0c125122f..615732e67 100644 --- a/cli/tests/076_info_json_deps_order.out +++ b/cli/tests/076_info_json_deps_order.out @@ -1,38 +1,85 @@ { - "compiled": null, - "depCount": 4, - "fileType": "TypeScript", - "files": { - "[WILDCARD]cli/tests/076_info_json_deps_order.ts": { - "deps": [ - "[WILDCARD]cli/tests/recursive_imports/A.ts" + "root": "file://[WILDCARD]/cli/tests/076_info_json_deps_order.ts", + "modules": [ + { + "specifier": "file://[WILDCARD]/cli/tests/076_info_json_deps_order.ts", + "dependencies": [ + { + "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": [ - "[WILDCARD]cli/tests/recursive_imports/B.ts", - "[WILDCARD]cli/tests/recursive_imports/common.ts" + { + "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts", + "dependencies": [ + { + "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": [ - "[WILDCARD]cli/tests/recursive_imports/C.ts", - "[WILDCARD]cli/tests/recursive_imports/common.ts" + { + "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/B.ts", + "dependencies": [ + { + "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": [ - "[WILDCARD]cli/tests/recursive_imports/A.ts", - "[WILDCARD]cli/tests/recursive_imports/common.ts" + { + "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/C.ts", + "dependencies": [ + { + "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": [], - "size": [WILDCARD] + { + "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/common.ts", + "dependencies": [], + "size": 34, + "mediaType": "TypeScript", + "local": "[WILDCARD]common.ts", + "checksum": "01b595d69514bfd001ba2cf421feabeaef559513f10697bf1a22781f8a8ed7f0" } - }, -[WILDCARD] + ], + "size": 440 +}
\ No newline at end of file diff --git a/cli/tests/info_missing_module.out b/cli/tests/info_missing_module.out index fd9b9b763..e01cd098e 100644 --- a/cli/tests/info_missing_module.out +++ b/cli/tests/info_missing_module.out @@ -1,2 +1,6 @@ -error: Cannot resolve module "file://[WILDCARD]/bad-module.js" from "file://[WILDCARD]/error_009_missing_js_module.js". - at file://[WILDCARD]/error_009_missing_js_module.js:1:0 +local: [WILDCARD]error_009_missing_js_module.js +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) diff --git a/cli/tests/info_recursive_imports_test.out b/cli/tests/info_recursive_imports_test.out index 4afd00544..7b53ed89b 100644 --- a/cli/tests/info_recursive_imports_test.out +++ b/cli/tests/info_recursive_imports_test.out @@ -1,12 +1,12 @@ local: [WILDCARD]info_recursive_imports_test.ts 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/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 [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 * diff --git a/cli/tests/info_type_import.out b/cli/tests/info_type_import.out index dc2a3dfe8..6b9869f1f 100644 --- a/cli/tests/info_type_import.out +++ b/cli/tests/info_type_import.out @@ -1,5 +1,5 @@ local: [WILDCARD]info_type_import.ts type: TypeScript -deps: 1 unique (total [WILDCARD]) +dependencies: 1 unique (total [WILDCARD]) [WILDCARD]info_type_import.ts ([WILDCARD]) └── [WILDCARD]type_and_code.ts ([WILDCARD]) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index c59bee2ce..0d076f180 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1732,7 +1732,7 @@ mod integration { 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!(str_output.contains("emit: ")); assert_eq!(output.stderr, b""); } @@ -3720,7 +3720,6 @@ console.log("finish"); itest!(info_missing_module { args: "info error_009_missing_js_module.js", output: "info_missing_module.out", - exit_code: 1, }); itest!(info_recursive_modules { diff --git a/cli/tools/coverage.rs b/cli/tools/coverage.rs index 1ec33affd..eb7c5e932 100644 --- a/cli/tools/coverage.rs +++ b/cli/tools/coverage.rs @@ -521,7 +521,7 @@ impl CoverageReporter for PrettyCoverageReporter { // Put a horizontal separator between disjoint runs of lines if let Some(last_line) = last_line { 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); } } |