summaryrefslogtreecommitdiff
path: root/cli/module_graph2.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/module_graph2.rs')
-rw-r--r--cli/module_graph2.rs189
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