diff options
Diffstat (limited to 'cli/tsc.rs')
-rw-r--r-- | cli/tsc.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/cli/tsc.rs b/cli/tsc.rs index 9f8216e52..2a1307432 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -302,6 +302,13 @@ impl CompiledFileMetadata { } } +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +struct TranspileSourceFile { + pub source_code: String, + pub file_name: String, +} + /// Emit a SHA256 hash based on source code, deno version and TS config. /// Used to check if a recompilation for source code is needed. fn source_code_version_hash( @@ -376,6 +383,13 @@ struct CompileResponse { build_info: Option<String>, stats: Option<Vec<Stat>>, } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct TranspileResponse { + diagnostics: Diagnostic, + emit_map: HashMap<String, EmittedSource>, + stats: Option<Vec<Stat>>, +} // TODO(bartlomieju): possible deduplicate once TS refactor is stabilized #[derive(Deserialize)] @@ -704,6 +718,72 @@ impl TsCompiler { Ok(output) } + pub async fn transpile( + &self, + global_state: GlobalState, + permissions: Permissions, + module_graph: ModuleGraph, + ) -> Result<(), ErrBox> { + let mut source_files: Vec<TranspileSourceFile> = Vec::new(); + for (_, value) in module_graph.iter() { + let url = Url::parse(&value.url).expect("Filename is not a valid url"); + if !value.url.ends_with(".d.ts") + && (!self.use_disk_cache || !self.has_compiled_source(&url)) + { + source_files.push(TranspileSourceFile { + source_code: value.source_code.clone(), + file_name: value.url.clone(), + }); + } + } + if source_files.is_empty() { + return Ok(()); + } + + let source_files_json = + serde_json::to_value(source_files).expect("Filed to serialize data"); + let compiler_config = self.config.clone(); + let cwd = std::env::current_dir().unwrap(); + let performance = match global_state.flags.log_level { + Some(Level::Debug) => true, + _ => false, + }; + let j = match (compiler_config.path, compiler_config.content) { + (Some(config_path), Some(config_data)) => json!({ + "config": str::from_utf8(&config_data).unwrap(), + "configPath": config_path, + "cwd": cwd, + "performance": performance, + "sourceFiles": source_files_json, + "type": msg::CompilerRequestType::Transpile, + }), + _ => json!({ + "performance": performance, + "sourceFiles": source_files_json, + "type": msg::CompilerRequestType::Transpile, + }), + }; + + let req_msg = j.to_string().into_boxed_str().into_boxed_bytes(); + + let msg = + execute_in_same_thread(global_state.clone(), permissions, req_msg) + .await?; + + let json_str = std::str::from_utf8(&msg).unwrap(); + + let transpile_response: TranspileResponse = serde_json::from_str(json_str)?; + + if !transpile_response.diagnostics.items.is_empty() { + return Err(ErrBox::from(transpile_response.diagnostics)); + } + + maybe_log_stats(transpile_response.stats); + + self.cache_emitted_files(transpile_response.emit_map)?; + Ok(()) + } + /// Get associated `CompiledFileMetadata` for given module if it exists. fn get_metadata(&self, url: &Url) -> Option<CompiledFileMetadata> { // Try to load cached version: @@ -1575,6 +1655,75 @@ mod tests { } #[tokio::test] + async fn test_transpile() { + let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .join("cli/tests/002_hello.ts"); + let specifier = + ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap(); + let out = SourceFile { + url: specifier.as_url().clone(), + filename: PathBuf::from(p.to_str().unwrap().to_string()), + media_type: msg::MediaType::TypeScript, + source_code: include_bytes!("./tests/002_hello.ts").to_vec(), + types_header: None, + }; + let dir = + deno_dir::DenoDir::new(Some(test_util::new_deno_dir().path().to_owned())) + .unwrap(); + let http_cache = http_cache::HttpCache::new(&dir.root.join("deps")); + let mock_state = GlobalState::mock( + vec![String::from("deno"), String::from("hello.ts")], + None, + ); + let file_fetcher = SourceFileFetcher::new( + http_cache, + true, + mock_state.flags.cache_blocklist.clone(), + false, + false, + None, + ) + .unwrap(); + + let mut module_graph_loader = ModuleGraphLoader::new( + file_fetcher.clone(), + None, + Permissions::allow_all(), + false, + false, + ); + module_graph_loader + .add_to_graph(&specifier, None) + .await + .expect("Failed to create graph"); + let module_graph = module_graph_loader.get_graph(); + + let ts_compiler = TsCompiler::new( + file_fetcher, + mock_state.flags.clone(), + dir.gen_cache.clone(), + ) + .unwrap(); + + let result = ts_compiler + .transpile(mock_state.clone(), Permissions::allow_all(), module_graph) + .await; + assert!(result.is_ok()); + let compiled_file = ts_compiler.get_compiled_module(&out.url).unwrap(); + let source_code = compiled_file.code; + assert!(source_code + .as_bytes() + .starts_with(b"console.log(\"Hello World\");")); + let mut lines: Vec<String> = + source_code.split('\n').map(|s| s.to_string()).collect(); + let last_line = lines.pop().unwrap(); + assert!(last_line + .starts_with("//# sourceMappingURL=data:application/json;base64")); + } + + #[tokio::test] async fn test_bundle() { let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() |