diff options
-rw-r--r-- | cli/js/compiler.ts | 10 | ||||
-rw-r--r-- | cli/module_graph.rs | 53 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 6 | ||||
-rw-r--r-- | cli/tests/type_directives_redirect.ts | 1 | ||||
-rw-r--r-- | cli/tests/type_directives_redirect.ts.out | 5 | ||||
-rwxr-xr-x | tools/http_server.py | 28 |
6 files changed, 90 insertions, 13 deletions
diff --git a/cli/js/compiler.ts b/cli/js/compiler.ts index a1189dc20..06117413a 100644 --- a/cli/js/compiler.ts +++ b/cli/js/compiler.ts @@ -585,7 +585,6 @@ function buildSourceFileCache( sourceFileMap: Record<string, SourceFileMapEntry> ): void { for (const entry of Object.values(sourceFileMap)) { - assert(entry.sourceCode.length > 0); SourceFile.addToCache({ url: entry.url, filename: entry.url, @@ -596,7 +595,15 @@ function buildSourceFileCache( for (const importDesc of entry.imports) { let mappedUrl = importDesc.resolvedSpecifier; const importedFile = sourceFileMap[importDesc.resolvedSpecifier]; + // IMPORTANT: due to HTTP redirects we might end up in situation + // where URL points to a file with completely different URL. + // In that case we take value of `redirect` field and cache + // resolved specifier pointing to the value of the redirect. + // It's not very elegant solution and should be rethinked. assert(importedFile); + if (importedFile.redirect) { + mappedUrl = importedFile.redirect; + } const isJsOrJsx = importedFile.mediaType === MediaType.JavaScript || importedFile.mediaType === MediaType.JSX; @@ -1032,6 +1039,7 @@ interface SourceFileMapEntry { url: string; sourceCode: string; mediaType: MediaType; + redirect?: string; imports: ImportDescriptor[]; referencedFiles: ReferenceDescriptor[]; libDirectives: ReferenceDescriptor[]; diff --git a/cli/module_graph.rs b/cli/module_graph.rs index e03468679..c63e6848f 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -19,6 +19,7 @@ use futures::FutureExt; use serde::Serialize; use serde::Serializer; use std::collections::HashMap; +use std::collections::HashSet; use std::hash::BuildHasher; use std::path::PathBuf; use std::pin::Pin; @@ -76,6 +77,7 @@ pub struct ReferenceDescriptor { pub struct ModuleGraphFile { pub specifier: String, pub url: String, + pub redirect: Option<String>, pub filename: String, pub imports: Vec<ImportDescriptor>, pub referenced_files: Vec<ReferenceDescriptor>, @@ -87,13 +89,14 @@ pub struct ModuleGraphFile { } type SourceFileFuture = - Pin<Box<dyn Future<Output = Result<SourceFile, ErrBox>>>>; + Pin<Box<dyn Future<Output = Result<(ModuleSpecifier, SourceFile), ErrBox>>>>; pub struct ModuleGraphLoader { permissions: Permissions, file_fetcher: SourceFileFetcher, maybe_import_map: Option<ImportMap>, pending_downloads: FuturesUnordered<SourceFileFuture>, + has_downloaded: HashSet<ModuleSpecifier>, pub graph: ModuleGraph, is_dyn_import: bool, analyze_dynamic_imports: bool, @@ -112,6 +115,7 @@ impl ModuleGraphLoader { permissions, maybe_import_map, pending_downloads: FuturesUnordered::new(), + has_downloaded: HashSet::new(), graph: ModuleGraph(HashMap::new()), is_dyn_import, analyze_dynamic_imports, @@ -131,8 +135,9 @@ impl ModuleGraphLoader { 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)?; + let (specifier, source_file) = + self.pending_downloads.next().await.unwrap()?; + self.visit_module(&specifier, source_file)?; if self.pending_downloads.is_empty() { break; } @@ -260,6 +265,7 @@ impl ModuleGraphLoader { ModuleGraphFile { specifier: specifier.to_string(), url: specifier.to_string(), + redirect: None, media_type: map_file_extension(&PathBuf::from(specifier.clone())) as i32, filename: specifier, @@ -281,7 +287,7 @@ impl ModuleGraphLoader { module_specifier: ModuleSpecifier, maybe_referrer: Option<ModuleSpecifier>, ) -> Result<(), ErrBox> { - if self.graph.0.contains_key(&module_specifier.to_string()) { + if self.has_downloaded.contains(&module_specifier) { return Ok(()); } @@ -319,6 +325,7 @@ impl ModuleGraphLoader { } } + self.has_downloaded.insert(module_specifier.clone()); let spec = module_specifier; let file_fetcher = self.file_fetcher.clone(); let perms = self.permissions.clone(); @@ -328,13 +335,7 @@ impl ModuleGraphLoader { 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 - }) + Ok((spec_.clone(), source_file)) } .boxed_local(); @@ -353,6 +354,33 @@ impl ModuleGraphLoader { let mut types_directives = vec![]; let mut type_headers = vec![]; + // IMPORTANT: source_file.url might be different than requested + // module_specifier because of HTTP redirects. In such + // situation we add an "empty" ModuleGraphFile with 'redirect' + // field set that will be later used in TS worker when building + // map of available source file. It will perform substitution + // for proper URL point to redirect target. + if module_specifier.as_url() != &source_file.url { + // TODO(bartlomieju): refactor, this is a band-aid + self.graph.0.insert( + module_specifier.to_string(), + ModuleGraphFile { + specifier: module_specifier.to_string(), + url: module_specifier.to_string(), + redirect: Some(source_file.url.to_string()), + filename: source_file.filename.to_str().unwrap().to_string(), + media_type: source_file.media_type as i32, + source_code: "".to_string(), + imports: vec![], + referenced_files: vec![], + lib_directives: vec![], + types_directives: vec![], + type_headers: vec![], + }, + ); + } + + let module_specifier = ModuleSpecifier::from(source_file.url.clone()); let source_code = String::from_utf8(source_file.source_code)?; if source_file.media_type == MediaType::JavaScript @@ -470,7 +498,8 @@ impl ModuleGraphLoader { module_specifier.to_string(), ModuleGraphFile { specifier: module_specifier.to_string(), - url: source_file.url.to_string(), + url: module_specifier.to_string(), + redirect: None, filename: source_file.filename.to_str().unwrap().to_string(), media_type: source_file.media_type as i32, source_code, diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 5419d3270..59e5f554d 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1565,6 +1565,12 @@ itest!(type_directives_js_main { exit_code: 0, }); +itest!(type_directives_redirect { + args: "run --reload type_directives_redirect.ts", + output: "type_directives_redirect.ts.out", + http_server: true, +}); + itest!(types { args: "types", output: "types.out", diff --git a/cli/tests/type_directives_redirect.ts b/cli/tests/type_directives_redirect.ts new file mode 100644 index 000000000..1756d5af9 --- /dev/null +++ b/cli/tests/type_directives_redirect.ts @@ -0,0 +1 @@ +import "http://localhost:4545/type_directives_redirect.js"; diff --git a/cli/tests/type_directives_redirect.ts.out b/cli/tests/type_directives_redirect.ts.out new file mode 100644 index 000000000..7473884f8 --- /dev/null +++ b/cli/tests/type_directives_redirect.ts.out @@ -0,0 +1,5 @@ +Download [WILDCARD]type_directives_redirect.js +Download [WILDCARD]xTypeScriptTypesRedirect.d.ts +Download [WILDCARD]xTypeScriptTypesRedirect.d.ts +Download [WILDCARD]xTypeScriptTypesRedirected.d.ts +Compile [WILDCARD]type_directives_redirect.ts diff --git a/tools/http_server.py b/tools/http_server.py index e2e9f2d98..346b319f8 100755 --- a/tools/http_server.py +++ b/tools/http_server.py @@ -122,6 +122,34 @@ class ContentTypeHandler(QuietSimpleHTTPRequestHandler): self.wfile.write(bytes("export const foo = 'foo';")) return + if "type_directives_redirect.js" in self.path: + self.protocol_version = "HTTP/1.1" + self.send_response(200, 'OK') + self.send_header('Content-type', 'application/javascript') + self.send_header( + 'X-TypeScript-Types', + 'http://localhost:4547/xTypeScriptTypesRedirect.d.ts') + self.end_headers() + self.wfile.write(bytes("export const foo = 'foo';")) + return + + if "xTypeScriptTypesRedirect.d.ts" in self.path: + self.protocol_version = "HTTP/1.1" + self.send_response(200, 'OK') + self.send_header('Content-type', 'application/typescript') + self.end_headers() + self.wfile.write( + bytes("import './xTypeScriptTypesRedirected.d.ts';")) + return + + if "xTypeScriptTypesRedirected.d.ts" in self.path: + self.protocol_version = "HTTP/1.1" + self.send_response(200, 'OK') + self.send_header('Content-type', 'application/typescript') + self.end_headers() + self.wfile.write(bytes("export const foo: 'foo';")) + return + if "xTypeScriptTypes.d.ts" in self.path: self.protocol_version = "HTTP/1.1" self.send_response(200, 'OK') |