summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-10-07 16:24:15 +1100
committerGitHub <noreply@github.com>2020-10-07 16:24:15 +1100
commit7ab645f512057d74fda2d0edf2d336cea3ed524e (patch)
tree7c208585997475cc6fa808d17d3c2b58c065c4cd
parentece400d13fdb38d7acaa57a17354538bd5d6a44c (diff)
refactor(cli): cleanups to new module graph (#7846)
-rw-r--r--cli/global_state.rs6
-rw-r--r--cli/main.rs2
-rw-r--r--cli/module_graph2.rs (renamed from cli/graph.rs)318
-rw-r--r--cli/specifier_handler.rs131
4 files changed, 148 insertions, 309 deletions
diff --git a/cli/global_state.rs b/cli/global_state.rs
index 336dcae00..9f5158332 100644
--- a/cli/global_state.rs
+++ b/cli/global_state.rs
@@ -3,8 +3,6 @@
use crate::deno_dir;
use crate::file_fetcher::SourceFileFetcher;
use crate::flags;
-use crate::graph::GraphBuilder;
-use crate::graph::TranspileOptions;
use crate::http_cache;
use crate::import_map::ImportMap;
use crate::inspector::InspectorServer;
@@ -12,6 +10,8 @@ use crate::lockfile::Lockfile;
use crate::media_type::MediaType;
use crate::module_graph::ModuleGraphFile;
use crate::module_graph::ModuleGraphLoader;
+use crate::module_graph2::GraphBuilder2;
+use crate::module_graph2::TranspileOptions;
use crate::permissions::Permissions;
use crate::specifier_handler::FetchHandler;
use crate::tsc::CompiledModule;
@@ -130,7 +130,7 @@ impl GlobalState {
// something that should be handled better in the future.
let handler =
Rc::new(RefCell::new(FetchHandler::new(self, permissions.clone())?));
- let mut builder = GraphBuilder::new(handler, maybe_import_map);
+ let mut builder = GraphBuilder2::new(handler, maybe_import_map);
builder.insert(&module_specifier).await?;
let mut graph = builder.get_graph(&self.lockfile)?;
diff --git a/cli/main.rs b/cli/main.rs
index 8672a5681..bd39d80bc 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -25,7 +25,6 @@ pub mod fmt_errors;
mod fs;
pub mod global_state;
mod global_timer;
-mod graph;
pub mod http_cache;
mod http_util;
mod import_map;
@@ -38,6 +37,7 @@ mod lockfile;
mod media_type;
mod metrics;
mod module_graph;
+mod module_graph2;
mod op_fetch_asset;
pub mod ops;
pub mod permissions;
diff --git a/cli/graph.rs b/cli/module_graph2.rs
index ed8f5cad7..b04f21200 100644
--- a/cli/graph.rs
+++ b/cli/module_graph2.rs
@@ -107,22 +107,6 @@ impl fmt::Display for GraphError {
impl Error for GraphError {}
-/// A trait, implemented by `Graph` that provides the interfaces that the
-/// compiler ops require to be able to retrieve information about the graph.
-pub trait ModuleProvider {
- /// Get the source for a given module specifier. If the module is not part
- /// of the graph, the result will be `None`.
- fn get_source(&self, specifier: &ModuleSpecifier) -> Option<String>;
- /// Given a string specifier and a referring module specifier, provide the
- /// resulting module specifier and media type for the module that is part of
- /// the graph.
- fn resolve(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- ) -> Result<(ModuleSpecifier, MediaType), AnyError>;
-}
-
/// An enum which represents the parsed out values of references in source code.
#[derive(Debug, Clone, Eq, PartialEq)]
enum TypeScriptReference {
@@ -420,23 +404,22 @@ pub struct TranspileOptions {
/// A dependency graph of modules, were the modules that have been inserted via
/// the builder will be loaded into the graph. Also provides an interface to
/// be able to manipulate and handle the graph.
-
#[derive(Debug)]
-pub struct Graph {
+pub struct Graph2 {
build_info: BuildInfoMap,
handler: Rc<RefCell<dyn SpecifierHandler>>,
modules: HashMap<ModuleSpecifier, Module>,
roots: Vec<ModuleSpecifier>,
}
-impl Graph {
+impl Graph2 {
/// Create a new instance of a graph, ready to have modules loaded it.
///
/// The argument `handler` is an instance of a structure that implements the
/// `SpecifierHandler` trait.
///
pub fn new(handler: Rc<RefCell<dyn SpecifierHandler>>) -> Self {
- Graph {
+ Graph2 {
build_info: HashMap::new(),
handler,
modules: HashMap::new(),
@@ -584,82 +567,15 @@ impl Graph {
}
}
-impl<'a> ModuleProvider for Graph {
- fn get_source(&self, specifier: &ModuleSpecifier) -> Option<String> {
- if let Some(module) = self.modules.get(specifier) {
- if let Ok(source) = module.source.to_string() {
- Some(source)
- } else {
- None
- }
- } else {
- None
- }
- }
-
- fn resolve(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- ) -> Result<(ModuleSpecifier, MediaType), AnyError> {
- if !self.modules.contains_key(referrer) {
- return Err(MissingSpecifier(referrer.to_owned()).into());
- }
- let module = self.modules.get(referrer).unwrap();
- if !module.dependencies.contains_key(specifier) {
- return Err(
- MissingDependency(referrer.to_owned(), specifier.to_owned()).into(),
- );
- }
- let dependency = module.dependencies.get(specifier).unwrap();
- // If there is a @deno-types pragma that impacts the dependency, then the
- // maybe_type property will be set with that specifier, otherwise we use the
- // specifier that point to the runtime code.
- let resolved_specifier =
- if let Some(type_specifier) = dependency.maybe_type.clone() {
- type_specifier
- } else if let Some(code_specifier) = dependency.maybe_code.clone() {
- code_specifier
- } else {
- return Err(
- MissingDependency(referrer.to_owned(), specifier.to_owned()).into(),
- );
- };
- if !self.modules.contains_key(&resolved_specifier) {
- return Err(
- MissingDependency(referrer.to_owned(), resolved_specifier.to_string())
- .into(),
- );
- }
- let dep_module = self.modules.get(&resolved_specifier).unwrap();
- // In the case that there is a X-TypeScript-Types or a triple-slash types,
- // then the `maybe_types` specifier will be populated and we should use that
- // instead.
- let result = if let Some((_, types)) = dep_module.maybe_types.clone() {
- if let Some(types_module) = self.modules.get(&types) {
- (types, types_module.media_type)
- } else {
- return Err(
- MissingDependency(referrer.to_owned(), types.to_string()).into(),
- );
- }
- } else {
- (resolved_specifier, dep_module.media_type)
- };
-
- Ok(result)
- }
-}
-
/// A structure for building a dependency graph of modules.
-pub struct GraphBuilder {
+pub struct GraphBuilder2 {
fetched: HashSet<ModuleSpecifier>,
- graph: Graph,
+ graph: Graph2,
maybe_import_map: Option<Rc<RefCell<ImportMap>>>,
pending: FuturesUnordered<FetchFuture>,
}
-impl GraphBuilder {
+impl GraphBuilder2 {
pub fn new(
handler: Rc<RefCell<dyn SpecifierHandler>>,
maybe_import_map: Option<ImportMap>,
@@ -669,8 +585,8 @@ impl GraphBuilder {
} else {
None
};
- GraphBuilder {
- graph: Graph::new(handler),
+ GraphBuilder2 {
+ graph: Graph2::new(handler),
fetched: HashSet::new(),
maybe_import_map: internal_import_map,
pending: FuturesUnordered::new(),
@@ -756,10 +672,15 @@ impl GraphBuilder {
/// lockfile can be provided, where if the sources in the graph do not match
/// the expected lockfile, the method with error instead of returning the
/// graph.
+ ///
+ /// TODO(@kitsonk) this should really be owned by the graph, but currently
+ /// the lockfile is behind a mutex in global_state, which makes it really
+ /// hard to not pass around as a reference, which if the Graph owned it, it
+ /// would need lifetime parameters and lifetime parameters are 😭
pub fn get_graph(
self,
maybe_lockfile: &Option<Mutex<Lockfile>>,
- ) -> Result<Graph, AnyError> {
+ ) -> Result<Graph2, AnyError> {
self.graph.lock(maybe_lockfile)?;
Ok(self.graph)
}
@@ -769,12 +690,136 @@ impl GraphBuilder {
mod tests {
use super::*;
- use crate::specifier_handler::tests::MockSpecifierHandler;
-
+ use deno_core::futures::future;
use std::env;
+ use std::fs;
use std::path::PathBuf;
use std::sync::Mutex;
+ /// This is a testing mock for `SpecifierHandler` that uses a special file
+ /// system renaming to mock local and remote modules as well as provides
+ /// "spies" for the critical methods for testing purposes.
+ #[derive(Debug, Default)]
+ pub struct MockSpecifierHandler {
+ pub fixtures: PathBuf,
+ pub build_info: HashMap<ModuleSpecifier, TextDocument>,
+ pub build_info_calls: Vec<(ModuleSpecifier, EmitType, TextDocument)>,
+ pub cache_calls: Vec<(
+ ModuleSpecifier,
+ EmitType,
+ TextDocument,
+ Option<TextDocument>,
+ )>,
+ pub deps_calls: Vec<(ModuleSpecifier, DependencyMap)>,
+ pub types_calls: Vec<(ModuleSpecifier, String)>,
+ pub version_calls: Vec<(ModuleSpecifier, String)>,
+ }
+
+ impl MockSpecifierHandler {
+ fn get_cache(
+ &self,
+ specifier: ModuleSpecifier,
+ ) -> Result<CachedModule, AnyError> {
+ let specifier_text = specifier
+ .to_string()
+ .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
+ }
+ }
+ "tsx" => MediaType::TSX,
+ "js" => MediaType::JavaScript,
+ "jsx" => MediaType::JSX,
+ _ => MediaType::Unknown,
+ };
+ let source =
+ TextDocument::new(fs::read(specifier_path)?, Option::<&str>::None);
+
+ Ok(CachedModule {
+ source,
+ specifier,
+ media_type,
+ ..CachedModule::default()
+ })
+ }
+ }
+
+ impl SpecifierHandler for MockSpecifierHandler {
+ fn fetch(&mut self, specifier: ModuleSpecifier) -> FetchFuture {
+ Box::pin(future::ready(self.get_cache(specifier)))
+ }
+ fn get_build_info(
+ &self,
+ specifier: &ModuleSpecifier,
+ _cache_type: &EmitType,
+ ) -> Result<Option<TextDocument>, AnyError> {
+ Ok(self.build_info.get(specifier).cloned())
+ }
+ fn set_cache(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ cache_type: &EmitType,
+ code: TextDocument,
+ maybe_map: Option<TextDocument>,
+ ) -> Result<(), AnyError> {
+ self.cache_calls.push((
+ specifier.clone(),
+ cache_type.clone(),
+ code,
+ maybe_map,
+ ));
+ Ok(())
+ }
+ fn set_types(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ types: String,
+ ) -> Result<(), AnyError> {
+ self.types_calls.push((specifier.clone(), types));
+ Ok(())
+ }
+ fn set_build_info(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ cache_type: &EmitType,
+ build_info: TextDocument,
+ ) -> Result<(), AnyError> {
+ self
+ .build_info
+ .insert(specifier.clone(), build_info.clone());
+ self.build_info_calls.push((
+ specifier.clone(),
+ cache_type.clone(),
+ build_info,
+ ));
+ Ok(())
+ }
+ fn set_deps(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ dependencies: DependencyMap,
+ ) -> Result<(), AnyError> {
+ self.deps_calls.push((specifier.clone(), dependencies));
+ Ok(())
+ }
+ fn set_version(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ version: String,
+ ) -> Result<(), AnyError> {
+ self.version_calls.push((specifier.clone(), version));
+ Ok(())
+ }
+ }
+
#[test]
fn test_get_version() {
let doc_a =
@@ -856,81 +901,6 @@ mod tests {
}
#[tokio::test]
- async fn test_graph_builder() {
- 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 = GraphBuilder::new(handler, None);
- let specifier =
- ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts")
- .expect("could not resolve module");
- builder
- .insert(&specifier)
- .await
- .expect("module not inserted");
- let graph = builder.get_graph(&None).expect("error getting graph");
- let actual = graph
- .resolve("./a.ts", &specifier)
- .expect("module to resolve");
- let expected = (
- ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a.ts")
- .expect("unable to resolve"),
- MediaType::TypeScript,
- );
- assert_eq!(actual, expected);
- }
-
- #[tokio::test]
- async fn test_graph_builder_import_map() {
- 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 import_map = ImportMap::from_json(
- "https://deno.land/x/import_map.ts",
- r#"{
- "imports": {
- "jquery": "./jquery.js",
- "lodash": "https://unpkg.com/lodash/index.js"
- }
- }"#,
- )
- .expect("could not load import map");
- let mut builder = GraphBuilder::new(handler, Some(import_map));
- let specifier =
- ModuleSpecifier::resolve_url_or_path("https://deno.land/x/import_map.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 actual_jquery = graph
- .resolve("jquery", &specifier)
- .expect("module to resolve");
- let expected_jquery = (
- ModuleSpecifier::resolve_url_or_path("https://deno.land/x/jquery.js")
- .expect("unable to resolve"),
- MediaType::JavaScript,
- );
- assert_eq!(actual_jquery, expected_jquery);
- let actual_lodash = graph
- .resolve("lodash", &specifier)
- .expect("module to resolve");
- let expected_lodash = (
- ModuleSpecifier::resolve_url_or_path("https://unpkg.com/lodash/index.js")
- .expect("unable to resolve"),
- MediaType::JavaScript,
- );
- assert_eq!(actual_lodash, expected_lodash);
- }
-
- #[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
@@ -945,7 +915,7 @@ mod tests {
fixtures,
..MockSpecifierHandler::default()
}));
- let mut builder = GraphBuilder::new(handler.clone(), None);
+ let mut builder = GraphBuilder2::new(handler.clone(), None);
let specifier =
ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts")
.expect("could not resolve module");
@@ -1008,7 +978,7 @@ mod tests {
fixtures: fixtures.clone(),
..MockSpecifierHandler::default()
}));
- let mut builder = GraphBuilder::new(handler.clone(), None);
+ let mut builder = GraphBuilder2::new(handler.clone(), None);
let specifier =
ModuleSpecifier::resolve_url_or_path("https://deno.land/x/transpile.tsx")
.expect("could not resolve module");
@@ -1054,7 +1024,7 @@ mod tests {
fixtures,
..MockSpecifierHandler::default()
}));
- let mut builder = GraphBuilder::new(handler.clone(), None);
+ let mut builder = GraphBuilder2::new(handler.clone(), None);
let specifier =
ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts")
.expect("could not resolve module");
@@ -1080,7 +1050,7 @@ mod tests {
fixtures,
..MockSpecifierHandler::default()
}));
- let mut builder = GraphBuilder::new(handler.clone(), None);
+ let mut builder = GraphBuilder2::new(handler.clone(), None);
let specifier =
ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts")
.expect("could not resolve module");
diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs
index 80fb28b19..c2f852ab6 100644
--- a/cli/specifier_handler.rs
+++ b/cli/specifier_handler.rs
@@ -372,140 +372,9 @@ impl SpecifierHandler for FetchHandler {
#[cfg(test)]
pub mod tests {
use super::*;
-
use crate::http_cache::HttpCache;
-
- use deno_core::futures::future;
- use std::fs;
- use std::path::PathBuf;
use tempfile::TempDir;
- /// This is a testing mock for `SpecifierHandler` that uses a special file
- /// system renaming to mock local and remote modules as well as provides
- /// "spies" for the critical methods for testing purposes.
- #[derive(Debug, Default)]
- pub struct MockSpecifierHandler {
- pub fixtures: PathBuf,
- pub build_info: HashMap<ModuleSpecifier, TextDocument>,
- pub build_info_calls: Vec<(ModuleSpecifier, EmitType, TextDocument)>,
- pub cache_calls: Vec<(
- ModuleSpecifier,
- EmitType,
- TextDocument,
- Option<TextDocument>,
- )>,
- pub deps_calls: Vec<(ModuleSpecifier, DependencyMap)>,
- pub types_calls: Vec<(ModuleSpecifier, String)>,
- pub version_calls: Vec<(ModuleSpecifier, String)>,
- }
-
- impl MockSpecifierHandler {}
-
- impl MockSpecifierHandler {
- fn get_cache(
- &self,
- specifier: ModuleSpecifier,
- ) -> Result<CachedModule, AnyError> {
- let specifier_text = specifier
- .to_string()
- .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
- }
- }
- "tsx" => MediaType::TSX,
- "js" => MediaType::JavaScript,
- "jsx" => MediaType::JSX,
- _ => MediaType::Unknown,
- };
- let source =
- TextDocument::new(fs::read(specifier_path)?, Option::<&str>::None);
-
- Ok(CachedModule {
- source,
- specifier,
- media_type,
- ..CachedModule::default()
- })
- }
- }
-
- impl SpecifierHandler for MockSpecifierHandler {
- fn fetch(&mut self, specifier: ModuleSpecifier) -> FetchFuture {
- Box::pin(future::ready(self.get_cache(specifier)))
- }
- fn get_build_info(
- &self,
- specifier: &ModuleSpecifier,
- _cache_type: &EmitType,
- ) -> Result<Option<TextDocument>, AnyError> {
- Ok(self.build_info.get(specifier).cloned())
- }
- fn set_cache(
- &mut self,
- specifier: &ModuleSpecifier,
- cache_type: &EmitType,
- code: TextDocument,
- maybe_map: Option<TextDocument>,
- ) -> Result<(), AnyError> {
- self.cache_calls.push((
- specifier.clone(),
- cache_type.clone(),
- code,
- maybe_map,
- ));
- Ok(())
- }
- fn set_types(
- &mut self,
- specifier: &ModuleSpecifier,
- types: String,
- ) -> Result<(), AnyError> {
- self.types_calls.push((specifier.clone(), types));
- Ok(())
- }
- fn set_build_info(
- &mut self,
- specifier: &ModuleSpecifier,
- cache_type: &EmitType,
- build_info: TextDocument,
- ) -> Result<(), AnyError> {
- self
- .build_info
- .insert(specifier.clone(), build_info.clone());
- self.build_info_calls.push((
- specifier.clone(),
- cache_type.clone(),
- build_info,
- ));
- Ok(())
- }
- fn set_deps(
- &mut self,
- specifier: &ModuleSpecifier,
- dependencies: DependencyMap,
- ) -> Result<(), AnyError> {
- self.deps_calls.push((specifier.clone(), dependencies));
- Ok(())
- }
- fn set_version(
- &mut self,
- specifier: &ModuleSpecifier,
- version: String,
- ) -> Result<(), AnyError> {
- self.version_calls.push((specifier.clone(), version));
- Ok(())
- }
- }
-
fn setup() -> (TempDir, FetchHandler) {
let temp_dir = TempDir::new().expect("could not setup");
let deno_dir = DenoDir::new(Some(temp_dir.path().to_path_buf()))