From 3116ce9478df8e3a8cd3f5a6b6617a6e4a0f47c2 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 28 Oct 2020 11:22:29 +1100 Subject: [PATCH] fix(cli): restore tripleslash lib refs support --- cli/diagnostics.rs | 15 ++- cli/main.rs | 2 +- cli/module_graph2.rs | 8 +- cli/op_fetch_asset.rs | 2 +- cli/program_state.rs | 2 +- cli/tests/tsc2/file_libref.ts | 7 ++ cli/tsc.rs | 2 +- cli/tsc/99_main_compiler.js | 2 +- cli/tsc2.rs | 198 +++++++++++++++++++--------------- 9 files changed, 140 insertions(+), 98 deletions(-) create mode 100644 cli/tests/tsc2/file_libref.ts diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs index ba53ce62333292..1cd4ea2343c778 100644 --- a/cli/diagnostics.rs +++ b/cli/diagnostics.rs @@ -343,8 +343,19 @@ impl fmt::Display for Diagnostic { } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Diagnostics(pub Vec); +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Diagnostics(Vec); + +impl Diagnostics { + #[cfg(test)] + pub fn new(diagnostics: Vec) -> Self { + Diagnostics(diagnostics) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} impl<'de> Deserialize<'de> for Diagnostics { fn deserialize(deserializer: D) -> Result diff --git a/cli/main.rs b/cli/main.rs index 75677d1fbb0e70..109f88202b3492 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -353,7 +353,7 @@ async fn bundle_command( if let Some(ignored_options) = maybe_ignored_options { eprintln!("{}", ignored_options); } - if !diagnostics.0.is_empty() { + if !diagnostics.is_empty() { return Err(generic_error(diagnostics.to_string())); } } diff --git a/cli/module_graph2.rs b/cli/module_graph2.rs index cc063c6c372555..6c25ca8f21b286 100644 --- a/cli/module_graph2.rs +++ b/cli/module_graph2.rs @@ -747,7 +747,7 @@ impl Graph2 { debug!("graph does not need to be checked or emitted."); return Ok(( Stats(Vec::new()), - Diagnostics(Vec::new()), + Diagnostics::default(), maybe_ignored_options, )); } @@ -786,7 +786,7 @@ impl Graph2 { graph.maybe_tsbuildinfo = response.maybe_tsbuildinfo; // Only process changes to the graph if there are no diagnostics and there // were files emitted. - if response.diagnostics.0.is_empty() && !response.emitted_files.is_empty() { + if response.diagnostics.is_empty() && !response.emitted_files.is_empty() { let mut codes = HashMap::new(); let mut maps = HashMap::new(); let check_js = config.get_check_js(); @@ -1694,7 +1694,7 @@ pub mod tests { .expect("should have checked"); assert!(maybe_ignored_options.is_none()); assert_eq!(stats.0.len(), 12); - assert!(diagnostics.0.is_empty()); + assert!(diagnostics.is_empty()); let h = handler.borrow(); assert_eq!(h.cache_calls.len(), 2); assert_eq!(h.tsbuildinfo_calls.len(), 1); @@ -1717,7 +1717,7 @@ pub mod tests { .expect("should have checked"); assert!(maybe_ignored_options.is_none()); assert_eq!(stats.0.len(), 12); - assert!(diagnostics.0.is_empty()); + assert!(diagnostics.is_empty()); let h = handler.borrow(); assert_eq!(h.cache_calls.len(), 0); assert_eq!(h.tsbuildinfo_calls.len(), 1); diff --git a/cli/op_fetch_asset.rs b/cli/op_fetch_asset.rs index 4ed77d423c33a1..3ff8b782ff38e6 100644 --- a/cli/op_fetch_asset.rs +++ b/cli/op_fetch_asset.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; -fn get_asset(name: &str) -> Option<&'static str> { +pub fn get_asset(name: &str) -> Option<&'static str> { macro_rules! inc { ($e:expr) => { Some(include_str!(concat!("dts/", $e))) diff --git a/cli/program_state.rs b/cli/program_state.rs index f8af957b69457a..9eeb72f235fd84 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -182,7 +182,7 @@ impl ProgramState { if let Some(ignored_options) = maybe_ignored_options { eprintln!("{}", ignored_options); } - if !diagnostics.0.is_empty() { + if !diagnostics.is_empty() { return Err(generic_error(diagnostics.to_string())); } }; diff --git a/cli/tests/tsc2/file_libref.ts b/cli/tests/tsc2/file_libref.ts new file mode 100644 index 00000000000000..8af5a83d07327e --- /dev/null +++ b/cli/tests/tsc2/file_libref.ts @@ -0,0 +1,7 @@ +/// +/// +/// + +export const div = document.createElement("div"); +div.innerHTML = `Hello World!`; +console.log(Deno.args); diff --git a/cli/tsc.rs b/cli/tsc.rs index 2597789defd066..801ac8e8f3ab34 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -613,7 +613,7 @@ pub async fn runtime_compile( execute_in_tsc(program_state.clone(), req_msg).map_err(extract_js_error)?; let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?; - if response.diagnostics.0.is_empty() && sources.is_none() { + if response.diagnostics.is_empty() && sources.is_none() { compiler.cache_emitted_files(response.emit_map)?; } diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 1248bad5af7baa..b286f596d0191e 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -369,7 +369,7 @@ delete Object.prototype.__proto__; return sourceFile; } - /** @type {{ data: string; hash: string; }} */ + /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */ const { data, hash, scriptKind } = core.jsonOpSync( "op_load", { specifier }, diff --git a/cli/tsc2.rs b/cli/tsc2.rs index 44754c030a691f..25767619e4f5b7 100644 --- a/cli/tsc2.rs +++ b/cli/tsc2.rs @@ -4,6 +4,8 @@ use crate::diagnostics::Diagnostics; use crate::media_type::MediaType; use crate::module_graph2::Graph2; use crate::module_graph2::Stats; +// TODO(@kitsonk) this needs to be refactored when we drop MG1 +use crate::op_fetch_asset::get_asset; use crate::tsc_config::TsConfig; use deno_core::error::anyhow; @@ -24,6 +26,19 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +fn get_maybe_hash( + maybe_source: &Option, + hash_data: &[Vec], +) -> Option { + if let Some(source) = maybe_source { + let mut data = vec![source.as_bytes().to_owned()]; + data.extend_from_slice(hash_data); + Some(crate::checksum::gen(&data)) + } else { + None + } +} + #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct EmittedFile { pub data: String, @@ -177,6 +192,12 @@ fn load(state: &mut State, args: Value) -> Result { hash = Some("1".to_string()); media_type = MediaType::TypeScript; Some("declare var a: any;\nexport = a;\n".to_string()) + } else if v.specifier.starts_with("asset:///") { + let name = v.specifier.replace("asset:///", ""); + let maybe_source = get_asset(&name).map(|s| s.to_string()); + hash = get_maybe_hash(&maybe_source, &state.hash_data); + media_type = MediaType::from(&v.specifier); + maybe_source } else { let graph = state.graph.borrow(); let specifier = @@ -191,11 +212,7 @@ fn load(state: &mut State, args: Value) -> Result { } else { MediaType::Unknown }; - if let Some(source) = &maybe_source { - let mut data = vec![source.as_bytes().to_owned()]; - data.extend_from_slice(&state.hash_data); - hash = Some(crate::checksum::gen(&data)); - } + hash = get_maybe_hash(&maybe_source, &state.hash_data); maybe_source }; @@ -396,6 +413,47 @@ mod tests { State::new(graph, hash_data, maybe_tsbuildinfo, HashMap::new()) } + async fn test_exec( + specifier: &ModuleSpecifier, + ) -> Result { + let hash_data = vec![b"something".to_vec()]; + let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let fixtures = c.join("tests/tsc2"); + let handler = Rc::new(RefCell::new(MockSpecifierHandler { + fixtures, + ..Default::default() + })); + let mut builder = GraphBuilder2::new(handler.clone(), None, None); + builder.add(&specifier, false).await?; + let graph = Rc::new(RefCell::new(builder.get_graph())); + let config = TsConfig::new(json!({ + "allowJs": true, + "checkJs": false, + "esModuleInterop": true, + "emitDecoratorMetadata": false, + "incremental": true, + "jsx": "react", + "jsxFactory": "React.createElement", + "jsxFragmentFactory": "React.Fragment", + "lib": ["deno.window"], + "module": "esnext", + "noEmit": true, + "outDir": "deno:///", + "strict": true, + "target": "esnext", + "tsBuildInfoFile": "deno:///.tsbuildinfo", + })); + let request = Request { + config, + debug: false, + graph, + hash_data, + maybe_tsbuildinfo: None, + root_names: vec![(specifier.clone(), MediaType::TypeScript)], + }; + exec(js::compiler_isolate_init(), request) + } + #[tokio::test] async fn test_create_hash() { let mut state = setup(None, Some(vec![b"something".to_vec()]), None).await; @@ -481,6 +539,36 @@ mod tests { ); } + #[derive(Debug, Deserialize)] + #[serde(rename_all = "camelCase")] + struct LoadResponse { + data: String, + hash: Option, + script_kind: i64, + } + + #[tokio::test] + async fn test_load_asset() { + let mut state = setup( + Some( + ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") + .unwrap(), + ), + None, + Some("some content".to_string()), + ) + .await; + let value = + load(&mut state, json!({ "specifier": "asset:///lib.dom.d.ts" })) + .expect("should have invoked op"); + let actual: LoadResponse = + serde_json::from_value(value).expect("failed to deserialize"); + let expected = get_asset("lib.dom.d.ts").unwrap(); + assert_eq!(actual.data, expected); + assert!(actual.hash.is_some()); + assert_eq!(actual.script_kind, 3); + } + #[tokio::test] async fn test_load_tsbuildinfo() { let mut state = setup( @@ -581,7 +669,7 @@ mod tests { assert_eq!( state.maybe_response, Some(RespondArgs { - diagnostics: Diagnostics(vec![Diagnostic { + diagnostics: Diagnostics::new(vec![Diagnostic { category: DiagnosticCategory::Error, code: 5023, start: None, @@ -601,50 +689,13 @@ mod tests { } #[tokio::test] - async fn test_exec() { + async fn test_exec_basic() { let specifier = ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a.ts").unwrap(); - let hash_data = vec![b"something".to_vec()]; - let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let fixtures = c.join("tests/tsc2"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { - fixtures, - ..MockSpecifierHandler::default() - })); - let mut builder = GraphBuilder2::new(handler.clone(), None, None); - builder - .add(&specifier, false) + let actual = test_exec(&specifier) .await - .expect("module not inserted"); - let graph = Rc::new(RefCell::new(builder.get_graph())); - let config = TsConfig::new(json!({ - "allowJs": true, - "checkJs": false, - "esModuleInterop": true, - "emitDecoratorMetadata": false, - "incremental": true, - "jsx": "react", - "jsxFactory": "React.createElement", - "jsxFragmentFactory": "React.Fragment", - "lib": ["deno.window"], - "module": "esnext", - "noEmit": true, - "outDir": "deno:///", - "strict": true, - "target": "esnext", - "tsBuildInfoFile": "deno:///.tsbuildinfo", - })); - let request = Request { - config, - debug: false, - graph, - hash_data, - maybe_tsbuildinfo: None, - root_names: vec![(specifier, MediaType::TypeScript)], - }; - let actual = exec(js::compiler_isolate_init(), request) - .expect("exec should have not errored"); - assert!(actual.diagnostics.0.is_empty()); + .expect("exec should not have errored"); + assert!(actual.diagnostics.is_empty()); assert!(actual.emitted_files.is_empty()); assert!(actual.maybe_tsbuildinfo.is_some()); assert_eq!(actual.stats.0.len(), 12); @@ -654,49 +705,22 @@ mod tests { async fn test_exec_reexport_dts() { let specifier = ModuleSpecifier::resolve_url_or_path("file:///reexports.ts").unwrap(); - let hash_data = vec![b"something".to_vec()]; - let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let fixtures = c.join("tests/tsc2"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { - fixtures, - ..MockSpecifierHandler::default() - })); - let mut builder = GraphBuilder2::new(handler.clone(), None, None); - builder - .add(&specifier, false) + let actual = test_exec(&specifier) .await - .expect("module not inserted"); - let graph = Rc::new(RefCell::new(builder.get_graph())); - let config = TsConfig::new(json!({ - "allowJs": true, - "checkJs": false, - "esModuleInterop": true, - "emitDecoratorMetadata": false, - "incremental": true, - "jsx": "react", - "jsxFactory": "React.createElement", - "jsxFragmentFactory": "React.Fragment", - "lib": ["deno.window"], - "module": "esnext", - "noEmit": true, - "outDir": "deno:///", - "strict": true, - "target": "esnext", - "tsBuildInfoFile": "deno:///.tsbuildinfo", - })); - let request = Request { - config, - debug: false, - graph, - hash_data, - maybe_tsbuildinfo: None, - root_names: vec![(specifier, MediaType::TypeScript)], - }; - let actual = exec(js::compiler_isolate_init(), request) - .expect("exec should have not errored"); - assert!(actual.diagnostics.0.is_empty()); + .expect("exec should not have errored"); + assert!(actual.diagnostics.is_empty()); assert!(actual.emitted_files.is_empty()); assert!(actual.maybe_tsbuildinfo.is_some()); assert_eq!(actual.stats.0.len(), 12); } + + #[tokio::test] + async fn fix_lib_ref() { + let specifier = + ModuleSpecifier::resolve_url_or_path("file:///libref.ts").unwrap(); + let actual = test_exec(&specifier) + .await + .expect("exec should not have errored"); + assert!(actual.diagnostics.is_empty()); + } }