diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-10-12 13:25:27 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-12 13:25:27 +1100 |
commit | e877b36072039811066726ab1619e7b5142f5680 (patch) | |
tree | 2c97db700b09f9c830adc206e3448102d42c3d6d /cli/module_graph2.rs | |
parent | fede13f2eb64f648e6c39aa01e2e0ede4e5be25e (diff) |
refactor(cli): move info subcommand over to new module graph (#7892)
Diffstat (limited to 'cli/module_graph2.rs')
-rw-r--r-- | cli/module_graph2.rs | 189 |
1 files changed, 174 insertions, 15 deletions
diff --git a/cli/module_graph2.rs b/cli/module_graph2.rs index 738fe188d..698586413 100644 --- a/cli/module_graph2.rs +++ b/cli/module_graph2.rs @@ -5,6 +5,10 @@ use crate::ast::parse; use crate::ast::Location; use crate::ast::ParsedModule; use crate::import_map::ImportMap; +use crate::info::ModuleGraphInfo; +use crate::info::ModuleInfo; +use crate::info::ModuleInfoMap; +use crate::info::ModuleInfoMapItem; use crate::lockfile::Lockfile; use crate::media_type::MediaType; use crate::specifier_handler::CachedModule; @@ -30,6 +34,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::error::Error; use std::fmt; +use std::path::PathBuf; use std::rc::Rc; use std::result; use std::sync::Mutex; @@ -162,13 +167,16 @@ struct Module { is_dirty: bool, is_hydrated: bool, is_parsed: bool, + maybe_emit_path: Option<PathBuf>, maybe_import_map: Option<Rc<RefCell<ImportMap>>>, + maybe_map_path: Option<PathBuf>, maybe_parsed_module: Option<ParsedModule>, maybe_types: Option<(String, ModuleSpecifier)>, maybe_version: Option<String>, media_type: MediaType, specifier: ModuleSpecifier, source: String, + source_path: PathBuf, } impl Default for Module { @@ -179,13 +187,16 @@ impl Default for Module { is_dirty: false, is_hydrated: false, is_parsed: false, + maybe_emit_path: None, maybe_import_map: None, + maybe_map_path: None, maybe_parsed_module: None, maybe_types: None, maybe_version: None, media_type: MediaType::Unknown, specifier: ModuleSpecifier::resolve_url("https://deno.land/x/").unwrap(), source: "".to_string(), + source_path: PathBuf::new(), } } } @@ -215,6 +226,9 @@ impl Module { pub fn hydrate(&mut self, cached_module: CachedModule) { self.media_type = cached_module.media_type; self.source = cached_module.source; + self.source_path = cached_module.source_path; + self.maybe_emit_path = cached_module.maybe_emit_path; + self.maybe_map_path = cached_module.maybe_map_path; if self.maybe_import_map.is_none() { if let Some(dependencies) = cached_module.maybe_dependencies { self.dependencies = dependencies; @@ -359,6 +373,10 @@ impl Module { pub fn set_version(&mut self, config: &[u8]) { self.maybe_version = Some(get_version(&self.source, version::DENO, config)) } + + pub fn size(&self) -> usize { + self.source.as_bytes().len() + } } #[derive(Clone, Debug, PartialEq)] @@ -421,6 +439,115 @@ impl Graph2 { } } + 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 = self.modules.get(specifier).unwrap(); + 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() + .map(|(specifier, module)| { + let mut deps = HashSet::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(), + }; + (specifier.clone(), item) + }) + .collect(); + + ModuleInfoMap::new(map) + } + + /// 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> { + if self.roots.is_empty() || self.roots.len() > 1 { + return Err(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 = self.modules.get(&module).unwrap(); + + 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(); + + Ok(ModuleGraphInfo { + compiled: m.maybe_emit_path.clone(), + dep_count: self.modules.len() - 1, + file_type: m.media_type, + files, + info, + local: m.source_path.clone(), + map: m.maybe_map_path.clone(), + module, + total_size, + }) + } + /// Update the handler with any modules that are marked as _dirty_ and update /// any build info if present. fn flush(&mut self, emit_type: &EmitType) -> Result<(), AnyError> { @@ -713,25 +840,26 @@ mod tests { .replace(":///", "_") .replace("://", "_") .replace("/", "-"); - let specifier_path = self.fixtures.join(specifier_text); - let media_type = - match specifier_path.extension().unwrap().to_str().unwrap() { - "ts" => { - if specifier_path.to_string_lossy().ends_with(".d.ts") { - MediaType::Dts - } else { - MediaType::TypeScript - } + let source_path = self.fixtures.join(specifier_text); + let media_type = match source_path.extension().unwrap().to_str().unwrap() + { + "ts" => { + if source_path.to_string_lossy().ends_with(".d.ts") { + MediaType::Dts + } else { + MediaType::TypeScript } - "tsx" => MediaType::TSX, - "js" => MediaType::JavaScript, - "jsx" => MediaType::JSX, - _ => MediaType::Unknown, - }; - let source = fs::read_to_string(specifier_path)?; + } + "tsx" => MediaType::TSX, + "js" => MediaType::JavaScript, + "jsx" => MediaType::JSX, + _ => MediaType::Unknown, + }; + let source = fs::read_to_string(&source_path)?; Ok(CachedModule { source, + source_path, specifier, media_type, ..CachedModule::default() @@ -880,6 +1008,37 @@ mod tests { } #[tokio::test] + async fn test_graph_info() { + let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let fixtures = c.join("tests/module_graph"); + let handler = Rc::new(RefCell::new(MockSpecifierHandler { + fixtures, + ..MockSpecifierHandler::default() + })); + let mut builder = GraphBuilder2::new(handler.clone(), None); + let specifier = + ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); + builder + .insert(&specifier) + .await + .expect("module not inserted"); + let graph = builder.get_graph(&None).expect("could not get graph"); + 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, + ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts").unwrap() + ); + assert_eq!(info.total_size, 344); + } + + #[tokio::test] async fn test_graph_transpile() { // This is a complex scenario of transpiling, where we have TypeScript // importing a JavaScript file (with type definitions) which imports |