mirror of
https://github.com/denoland/deno.git
synced 2024-10-30 09:08:00 -04:00
9d63772fe5
This commit completely overhauls how module analysis is performed in TS compiler by moving the logic to Rust. In the current setup module analysis is performed using "ts.preProcessFile" API in a special TS compiler worker running on a separate thread. "ts.preProcessFile" allowed us to build a lot of functionality in CLI including X-TypeScript-Types header support and @deno-types directive support. Unfortunately at the same time complexity of the ops required to perform supporting tasks exploded and caused some hidden permission escapes. This PR introduces "ModuleGraphLoader" which can parse source and load recursively all dependent source files; as well as declaration files. All dependencies used in TS compiler and now fetched and collected upfront in Rust before spinning up TS compiler. To achieve feature parity with existing APIs this commit includes a lot of changes: * add "ModuleGraphLoader" - can fetch local and remote sources - parses source code using SWC and extracts imports, exports, file references, special headers - this struct inherited all of the hidden complexity and cruft from TS version and requires several follow up PRs * rewrite cli/tsc.rs to perform module analysis upfront and send all required source code to TS worker in one message * remove op_resolve_modules and op_fetch_source_files from cli/ops/compiler.rs * run TS worker on the same thread
732 lines
22 KiB
Rust
732 lines
22 KiB
Rust
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use crate::file_fetcher::SourceFile;
|
|
use crate::file_fetcher::SourceFileFetcher;
|
|
use crate::import_map::ImportMap;
|
|
use crate::msg::MediaType;
|
|
use crate::op_error::OpError;
|
|
use crate::permissions::Permissions;
|
|
use crate::swc_util::analyze_dependencies_and_references;
|
|
use crate::swc_util::TsReferenceKind;
|
|
use crate::tsc::get_available_libs;
|
|
use deno_core::ErrBox;
|
|
use deno_core::ModuleSpecifier;
|
|
use futures::stream::FuturesUnordered;
|
|
use futures::stream::StreamExt;
|
|
use futures::Future;
|
|
use futures::FutureExt;
|
|
use serde::Serialize;
|
|
use serde::Serializer;
|
|
use std::collections::HashMap;
|
|
use std::hash::BuildHasher;
|
|
use std::pin::Pin;
|
|
|
|
fn serialize_module_specifier<S>(
|
|
spec: &ModuleSpecifier,
|
|
s: S,
|
|
) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
s.serialize_str(&spec.to_string())
|
|
}
|
|
|
|
fn serialize_option_module_specifier<S>(
|
|
maybe_spec: &Option<ModuleSpecifier>,
|
|
s: S,
|
|
) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
if let Some(spec) = maybe_spec {
|
|
serialize_module_specifier(spec, s)
|
|
} else {
|
|
s.serialize_none()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ModuleGraph(HashMap<String, ModuleGraphFile>);
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ImportDescriptor {
|
|
specifier: String,
|
|
#[serde(serialize_with = "serialize_module_specifier")]
|
|
resolved_specifier: ModuleSpecifier,
|
|
// These two fields are for support of @deno-types directive
|
|
// directly prepending import statement
|
|
type_directive: Option<String>,
|
|
#[serde(serialize_with = "serialize_option_module_specifier")]
|
|
resolved_type_directive: Option<ModuleSpecifier>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ReferenceDescriptor {
|
|
specifier: String,
|
|
#[serde(serialize_with = "serialize_module_specifier")]
|
|
resolved_specifier: ModuleSpecifier,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ModuleGraphFile {
|
|
pub specifier: String,
|
|
pub url: String,
|
|
pub filename: String,
|
|
pub imports: Vec<ImportDescriptor>,
|
|
pub referenced_files: Vec<ReferenceDescriptor>,
|
|
pub lib_directives: Vec<ReferenceDescriptor>,
|
|
pub types_directives: Vec<ReferenceDescriptor>,
|
|
pub type_headers: Vec<ReferenceDescriptor>,
|
|
pub media_type: i32,
|
|
pub source_code: String,
|
|
}
|
|
|
|
type SourceFileFuture =
|
|
Pin<Box<dyn Future<Output = Result<SourceFile, ErrBox>>>>;
|
|
|
|
pub struct ModuleGraphLoader {
|
|
permissions: Permissions,
|
|
file_fetcher: SourceFileFetcher,
|
|
maybe_import_map: Option<ImportMap>,
|
|
pending_downloads: FuturesUnordered<SourceFileFuture>,
|
|
pub graph: ModuleGraph,
|
|
is_dyn_import: bool,
|
|
analyze_dynamic_imports: bool,
|
|
}
|
|
|
|
impl ModuleGraphLoader {
|
|
pub fn new(
|
|
file_fetcher: SourceFileFetcher,
|
|
maybe_import_map: Option<ImportMap>,
|
|
permissions: Permissions,
|
|
is_dyn_import: bool,
|
|
analyze_dynamic_imports: bool,
|
|
) -> Self {
|
|
Self {
|
|
file_fetcher,
|
|
permissions,
|
|
maybe_import_map,
|
|
pending_downloads: FuturesUnordered::new(),
|
|
graph: ModuleGraph(HashMap::new()),
|
|
is_dyn_import,
|
|
analyze_dynamic_imports,
|
|
}
|
|
}
|
|
|
|
/// This method is used to add specified module and all of its
|
|
/// dependencies to the graph.
|
|
///
|
|
/// It resolves when all dependent modules have been fetched and analyzed.
|
|
///
|
|
/// This method can be called multiple times.
|
|
pub async fn add_to_graph(
|
|
&mut self,
|
|
specifier: &ModuleSpecifier,
|
|
) -> Result<(), ErrBox> {
|
|
self.download_module(specifier.clone(), None)?;
|
|
|
|
loop {
|
|
let source_file = self.pending_downloads.next().await.unwrap()?;
|
|
self.visit_module(&source_file.url.clone().into(), source_file)?;
|
|
if self.pending_downloads.is_empty() {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// This method is used to create a graph from in-memory files stored in
|
|
/// a hash map. Useful for creating module graph for code received from
|
|
/// the runtime.
|
|
pub fn build_local_graph<S: BuildHasher>(
|
|
&mut self,
|
|
_root_name: &str,
|
|
source_map: &HashMap<String, String, S>,
|
|
) -> Result<(), ErrBox> {
|
|
for (spec, source_code) in source_map.iter() {
|
|
self.visit_memory_module(spec.to_string(), source_code.to_string())?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Consumes the loader and returns created graph.
|
|
pub fn get_graph(self) -> HashMap<String, ModuleGraphFile> {
|
|
self.graph.0
|
|
}
|
|
|
|
fn visit_memory_module(
|
|
&mut self,
|
|
specifier: String,
|
|
source_code: String,
|
|
) -> Result<(), ErrBox> {
|
|
let mut imports = vec![];
|
|
let mut referenced_files = vec![];
|
|
let mut lib_directives = vec![];
|
|
let mut types_directives = vec![];
|
|
|
|
// FIXME(bartlomieju):
|
|
// The resolveModules op only handles fully qualified URLs for referrer.
|
|
// However we will have cases where referrer is "/foo.ts". We add this dummy
|
|
// prefix "memory://" in order to use resolution logic.
|
|
let module_specifier =
|
|
if let Ok(spec) = ModuleSpecifier::resolve_url(&specifier) {
|
|
spec
|
|
} else {
|
|
ModuleSpecifier::resolve_url(&format!("memory://{}", specifier))?
|
|
};
|
|
|
|
let (import_descs, ref_descs) = analyze_dependencies_and_references(
|
|
&source_code,
|
|
self.analyze_dynamic_imports,
|
|
)?;
|
|
|
|
for import_desc in import_descs {
|
|
let maybe_resolved =
|
|
if let Some(import_map) = self.maybe_import_map.as_ref() {
|
|
import_map
|
|
.resolve(&import_desc.specifier, &module_specifier.to_string())?
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let resolved_specifier = if let Some(resolved) = maybe_resolved {
|
|
resolved
|
|
} else {
|
|
ModuleSpecifier::resolve_import(
|
|
&import_desc.specifier,
|
|
&module_specifier.to_string(),
|
|
)?
|
|
};
|
|
|
|
let resolved_type_directive =
|
|
if let Some(types_specifier) = import_desc.deno_types.as_ref() {
|
|
Some(ModuleSpecifier::resolve_import(
|
|
&types_specifier,
|
|
&module_specifier.to_string(),
|
|
)?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let import_descriptor = ImportDescriptor {
|
|
specifier: import_desc.specifier.to_string(),
|
|
resolved_specifier,
|
|
type_directive: import_desc.deno_types,
|
|
resolved_type_directive,
|
|
};
|
|
|
|
imports.push(import_descriptor);
|
|
}
|
|
|
|
let available_libs = get_available_libs();
|
|
|
|
for ref_desc in ref_descs {
|
|
if available_libs.contains(&ref_desc.specifier) {
|
|
continue;
|
|
}
|
|
|
|
let resolved_specifier = ModuleSpecifier::resolve_import(
|
|
&ref_desc.specifier,
|
|
&module_specifier.to_string(),
|
|
)?;
|
|
|
|
let reference_descriptor = ReferenceDescriptor {
|
|
specifier: ref_desc.specifier.to_string(),
|
|
resolved_specifier,
|
|
};
|
|
|
|
match ref_desc.kind {
|
|
TsReferenceKind::Lib => {
|
|
lib_directives.push(reference_descriptor);
|
|
}
|
|
TsReferenceKind::Types => {
|
|
types_directives.push(reference_descriptor);
|
|
}
|
|
TsReferenceKind::Path => {
|
|
referenced_files.push(reference_descriptor);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.graph.0.insert(
|
|
module_specifier.to_string(),
|
|
ModuleGraphFile {
|
|
specifier: specifier.to_string(),
|
|
url: specifier.to_string(),
|
|
filename: specifier,
|
|
// ignored, it's set in TS worker
|
|
media_type: MediaType::JavaScript as i32,
|
|
source_code,
|
|
imports,
|
|
referenced_files,
|
|
lib_directives,
|
|
types_directives,
|
|
type_headers: vec![],
|
|
},
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn download_module(
|
|
&mut self,
|
|
module_specifier: ModuleSpecifier,
|
|
maybe_referrer: Option<ModuleSpecifier>,
|
|
) -> Result<(), ErrBox> {
|
|
if self.graph.0.contains_key(&module_specifier.to_string()) {
|
|
return Ok(());
|
|
}
|
|
|
|
if !self.is_dyn_import {
|
|
// Verify that remote file doesn't try to statically import local file.
|
|
if let Some(referrer) = maybe_referrer.as_ref() {
|
|
let referrer_url = referrer.as_url();
|
|
match referrer_url.scheme() {
|
|
"http" | "https" => {
|
|
let specifier_url = module_specifier.as_url();
|
|
match specifier_url.scheme() {
|
|
"http" | "https" => {}
|
|
_ => {
|
|
let e = OpError::permission_denied("Remote module are not allowed to statically import local modules. Use dynamic import instead.".to_string());
|
|
return Err(e.into());
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
let spec = module_specifier;
|
|
let file_fetcher = self.file_fetcher.clone();
|
|
let perms = self.permissions.clone();
|
|
|
|
let load_future = async move {
|
|
let spec_ = spec.clone();
|
|
let source_file = file_fetcher
|
|
.fetch_source_file(&spec_, maybe_referrer, perms)
|
|
.await?;
|
|
// FIXME(bartlomieju):
|
|
// because of redirects we may end up with wrong URL,
|
|
// substitute with original one
|
|
Ok(SourceFile {
|
|
url: spec_.as_url().to_owned(),
|
|
..source_file
|
|
})
|
|
}
|
|
.boxed_local();
|
|
|
|
self.pending_downloads.push(load_future);
|
|
Ok(())
|
|
}
|
|
|
|
fn visit_module(
|
|
&mut self,
|
|
module_specifier: &ModuleSpecifier,
|
|
source_file: SourceFile,
|
|
) -> Result<(), ErrBox> {
|
|
let mut imports = vec![];
|
|
let mut referenced_files = vec![];
|
|
let mut lib_directives = vec![];
|
|
let mut types_directives = vec![];
|
|
let mut type_headers = vec![];
|
|
|
|
let source_code = String::from_utf8(source_file.source_code)?;
|
|
|
|
if source_file.media_type == MediaType::JavaScript
|
|
|| source_file.media_type == MediaType::TypeScript
|
|
{
|
|
if let Some(types_specifier) = source_file.types_header {
|
|
let type_header = ReferenceDescriptor {
|
|
specifier: types_specifier.to_string(),
|
|
resolved_specifier: ModuleSpecifier::resolve_import(
|
|
&types_specifier,
|
|
&module_specifier.to_string(),
|
|
)?,
|
|
};
|
|
self.download_module(
|
|
type_header.resolved_specifier.clone(),
|
|
Some(module_specifier.clone()),
|
|
)?;
|
|
type_headers.push(type_header);
|
|
}
|
|
|
|
let (import_descs, ref_descs) = analyze_dependencies_and_references(
|
|
&source_code,
|
|
self.analyze_dynamic_imports,
|
|
)?;
|
|
|
|
for import_desc in import_descs {
|
|
let maybe_resolved =
|
|
if let Some(import_map) = self.maybe_import_map.as_ref() {
|
|
import_map
|
|
.resolve(&import_desc.specifier, &module_specifier.to_string())?
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let resolved_specifier = if let Some(resolved) = maybe_resolved {
|
|
resolved
|
|
} else {
|
|
ModuleSpecifier::resolve_import(
|
|
&import_desc.specifier,
|
|
&module_specifier.to_string(),
|
|
)?
|
|
};
|
|
|
|
let resolved_type_directive =
|
|
if let Some(types_specifier) = import_desc.deno_types.as_ref() {
|
|
Some(ModuleSpecifier::resolve_import(
|
|
&types_specifier,
|
|
&module_specifier.to_string(),
|
|
)?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let import_descriptor = ImportDescriptor {
|
|
specifier: import_desc.specifier.to_string(),
|
|
resolved_specifier,
|
|
type_directive: import_desc.deno_types,
|
|
resolved_type_directive,
|
|
};
|
|
|
|
self.download_module(
|
|
import_descriptor.resolved_specifier.clone(),
|
|
Some(module_specifier.clone()),
|
|
)?;
|
|
|
|
if let Some(type_dir_url) =
|
|
import_descriptor.resolved_type_directive.as_ref()
|
|
{
|
|
self.download_module(
|
|
type_dir_url.clone(),
|
|
Some(module_specifier.clone()),
|
|
)?;
|
|
}
|
|
|
|
imports.push(import_descriptor);
|
|
}
|
|
|
|
let available_libs = get_available_libs();
|
|
|
|
for ref_desc in ref_descs {
|
|
if available_libs.contains(&ref_desc.specifier) {
|
|
continue;
|
|
}
|
|
|
|
let resolved_specifier = ModuleSpecifier::resolve_import(
|
|
&ref_desc.specifier,
|
|
&module_specifier.to_string(),
|
|
)?;
|
|
|
|
let reference_descriptor = ReferenceDescriptor {
|
|
specifier: ref_desc.specifier.to_string(),
|
|
resolved_specifier,
|
|
};
|
|
|
|
self.download_module(
|
|
reference_descriptor.resolved_specifier.clone(),
|
|
Some(module_specifier.clone()),
|
|
)?;
|
|
|
|
match ref_desc.kind {
|
|
TsReferenceKind::Lib => {
|
|
lib_directives.push(reference_descriptor);
|
|
}
|
|
TsReferenceKind::Types => {
|
|
types_directives.push(reference_descriptor);
|
|
}
|
|
TsReferenceKind::Path => {
|
|
referenced_files.push(reference_descriptor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.graph.0.insert(
|
|
module_specifier.to_string(),
|
|
ModuleGraphFile {
|
|
specifier: module_specifier.to_string(),
|
|
url: source_file.url.to_string(),
|
|
filename: source_file.filename.to_str().unwrap().to_string(),
|
|
media_type: source_file.media_type as i32,
|
|
source_code,
|
|
imports,
|
|
referenced_files,
|
|
lib_directives,
|
|
types_directives,
|
|
type_headers,
|
|
},
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::GlobalState;
|
|
|
|
async fn build_graph(
|
|
module_specifier: &ModuleSpecifier,
|
|
) -> Result<HashMap<String, ModuleGraphFile>, ErrBox> {
|
|
let global_state = GlobalState::new(Default::default()).unwrap();
|
|
let mut graph_loader = ModuleGraphLoader::new(
|
|
global_state.file_fetcher.clone(),
|
|
None,
|
|
Permissions::allow_all(),
|
|
false,
|
|
false,
|
|
);
|
|
graph_loader.add_to_graph(&module_specifier).await?;
|
|
Ok(graph_loader.get_graph())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_graph_fetch() {
|
|
let http_server_guard = crate::test_util::http_server();
|
|
|
|
let module_specifier = ModuleSpecifier::resolve_url_or_path(
|
|
"http://localhost:4545/cli/tests/019_media_types.ts",
|
|
)
|
|
.unwrap();
|
|
let graph = build_graph(&module_specifier)
|
|
.await
|
|
.expect("Failed to build graph");
|
|
|
|
let a = graph
|
|
.get("http://localhost:4545/cli/tests/019_media_types.ts")
|
|
.unwrap();
|
|
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_text_ecmascript.j3.js"
|
|
));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_video_vdn.t2.ts"
|
|
));
|
|
assert!(graph.contains_key("http://localhost:4545/cli/tests/subdir/mt_application_x_typescript.t4.ts"));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_video_mp2t.t3.ts"
|
|
));
|
|
assert!(graph.contains_key("http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js"));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js"
|
|
));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_text_javascript.j1.js"
|
|
));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/mt_text_typescript.t1.ts"
|
|
));
|
|
|
|
assert_eq!(
|
|
serde_json::to_value(&a.imports).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_text_typescript.t1.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_text_typescript.t1.ts",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_video_vdn.t2.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_video_vdn.t2.ts",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_video_mp2t.t3.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_video_mp2t.t3.ts",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_application_x_typescript.t4.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_application_x_typescript.t4.ts",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_text_javascript.j1.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_text_javascript.j1.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_application_ecmascript.j2.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_text_ecmascript.j3.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_text_ecmascript.j3.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
{
|
|
"specifier": "http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
])
|
|
);
|
|
drop(http_server_guard);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_graph_type_references() {
|
|
let http_server_guard = crate::test_util::http_server();
|
|
|
|
let module_specifier = ModuleSpecifier::resolve_url_or_path(
|
|
"http://localhost:4545/cli/tests/type_definitions.ts",
|
|
)
|
|
.unwrap();
|
|
|
|
let graph = build_graph(&module_specifier)
|
|
.await
|
|
.expect("Failed to build graph");
|
|
|
|
eprintln!("json {:#?}", serde_json::to_value(&graph).unwrap());
|
|
|
|
let a = graph
|
|
.get("http://localhost:4545/cli/tests/type_definitions.ts")
|
|
.unwrap();
|
|
assert_eq!(
|
|
serde_json::to_value(&a.imports).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "./type_definitions/foo.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/type_definitions/foo.js",
|
|
"typeDirective": "./type_definitions/foo.d.ts",
|
|
"resolvedTypeDirective": "http://localhost:4545/cli/tests/type_definitions/foo.d.ts"
|
|
},
|
|
{
|
|
"specifier": "./type_definitions/fizz.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/type_definitions/fizz.js",
|
|
"typeDirective": "./type_definitions/fizz.d.ts",
|
|
"resolvedTypeDirective": "http://localhost:4545/cli/tests/type_definitions/fizz.d.ts"
|
|
},
|
|
{
|
|
"specifier": "./type_definitions/qat.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/type_definitions/qat.ts",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
},
|
|
])
|
|
);
|
|
assert!(graph
|
|
.contains_key("http://localhost:4545/cli/tests/type_definitions/foo.js"));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/type_definitions/foo.d.ts"
|
|
));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/type_definitions/fizz.js"
|
|
));
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/type_definitions/fizz.d.ts"
|
|
));
|
|
assert!(graph
|
|
.contains_key("http://localhost:4545/cli/tests/type_definitions/qat.ts"));
|
|
|
|
drop(http_server_guard);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_graph_type_references2() {
|
|
let http_server_guard = crate::test_util::http_server();
|
|
|
|
let module_specifier = ModuleSpecifier::resolve_url_or_path(
|
|
"http://localhost:4545/cli/tests/type_directives_02.ts",
|
|
)
|
|
.unwrap();
|
|
|
|
let graph = build_graph(&module_specifier)
|
|
.await
|
|
.expect("Failed to build graph");
|
|
|
|
eprintln!("{:#?}", serde_json::to_value(&graph).unwrap());
|
|
|
|
let a = graph
|
|
.get("http://localhost:4545/cli/tests/type_directives_02.ts")
|
|
.unwrap();
|
|
assert_eq!(
|
|
serde_json::to_value(&a.imports).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "./subdir/type_reference.js",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/type_reference.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
}
|
|
])
|
|
);
|
|
|
|
assert!(graph.contains_key(
|
|
"http://localhost:4545/cli/tests/subdir/type_reference.d.ts"
|
|
));
|
|
|
|
let b = graph
|
|
.get("http://localhost:4545/cli/tests/subdir/type_reference.js")
|
|
.unwrap();
|
|
assert_eq!(
|
|
serde_json::to_value(&b.types_directives).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "./type_reference.d.ts",
|
|
"resolvedSpecifier": "http://localhost:4545/cli/tests/subdir/type_reference.d.ts",
|
|
}
|
|
])
|
|
);
|
|
drop(http_server_guard);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_graph_type_references3() {
|
|
let http_server_guard = crate::test_util::http_server();
|
|
|
|
let module_specifier = ModuleSpecifier::resolve_url_or_path(
|
|
"http://localhost:4545/cli/tests/type_directives_01.ts",
|
|
)
|
|
.unwrap();
|
|
|
|
let graph = build_graph(&module_specifier)
|
|
.await
|
|
.expect("Failed to build graph");
|
|
|
|
let ts = graph
|
|
.get("http://localhost:4545/cli/tests/type_directives_01.ts")
|
|
.unwrap();
|
|
assert_eq!(
|
|
serde_json::to_value(&ts.imports).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "http://127.0.0.1:4545/xTypeScriptTypes.js",
|
|
"resolvedSpecifier": "http://127.0.0.1:4545/xTypeScriptTypes.js",
|
|
"typeDirective": null,
|
|
"resolvedTypeDirective": null,
|
|
}
|
|
])
|
|
);
|
|
|
|
let headers = graph
|
|
.get("http://127.0.0.1:4545/xTypeScriptTypes.js")
|
|
.unwrap();
|
|
assert_eq!(
|
|
serde_json::to_value(&headers.type_headers).unwrap(),
|
|
json!([
|
|
{
|
|
"specifier": "./xTypeScriptTypes.d.ts",
|
|
"resolvedSpecifier": "http://127.0.0.1:4545/xTypeScriptTypes.d.ts"
|
|
}
|
|
])
|
|
);
|
|
drop(http_server_guard);
|
|
}
|
|
}
|