diff --git a/cli/flags.rs b/cli/flags.rs index 59bcb5cd0a..fbfb2fe2ff 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -3,6 +3,7 @@ use clap::App; use clap::AppSettings; use clap::Arg; +use clap::ArgGroup; use clap::ArgMatches; use clap::ArgSettings; use clap::SubCommand; @@ -37,6 +38,8 @@ pub enum DenoSubcommand { }, Cache { files: Vec, + worker_specifiers: Option>, + test_patterns: Option>, }, Fmt { check: bool, @@ -481,10 +484,20 @@ fn cache_parse(flags: &mut Flags, matches: &clap::ArgMatches) { compile_args_parse(flags, matches); let files = matches .values_of("file") - .unwrap() + .unwrap_or_default() .map(String::from) .collect(); - flags.subcommand = DenoSubcommand::Cache { files }; + let worker_specifiers = matches + .values_of("worker") + .map(|v| v.map(String::from).collect()); + let test_patterns = matches + .values_of("test") + .map(|v| v.map(String::from).collect()); + flags.subcommand = DenoSubcommand::Cache { + files, + worker_specifiers, + test_patterns, + }; } fn lock_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) { @@ -871,11 +884,31 @@ TypeScript compiler cache: Subdirectory containing TS compiler output.", fn cache_subcommand<'a, 'b>() -> App<'a, 'b> { compile_args(SubCommand::with_name("cache")) + .arg(Arg::with_name("file").takes_value(true).multiple(true)) .arg( - Arg::with_name("file") + Arg::with_name("worker") + .long("worker") + .help("Compile as Deno web workers") .takes_value(true) - .required(true) - .min_values(1), + .value_name("WORKER_SPECIFIERS") + .min_values(1) + .use_delimiter(true), + ) + .arg( + Arg::with_name("test") + .long("test") + .help("Includes files captured by: deno test ") + .takes_value(true) + .value_name("PATTERN_LIST") + .min_values(0) + .use_delimiter(true) + .require_equals(true), + ) + .group( + ArgGroup::with_name("target") + .args(&["file", "test", "worker"]) + .multiple(true) + .required(true), ) .about("Cache the dependencies") .long_about( @@ -1913,6 +1946,52 @@ mod tests { Flags { subcommand: DenoSubcommand::Cache { files: svec!["script.ts"], + worker_specifiers: None, + test_patterns: None, + }, + ..Flags::default() + } + ); + + let r = flags_from_vec_safe(svec!["deno", "cache", "--test"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Cache { + files: svec![], + worker_specifiers: None, + test_patterns: Some(svec![]), + }, + ..Flags::default() + } + ); + + let r = flags_from_vec_safe(svec!["deno", "cache", "--test=a,b"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Cache { + files: svec![], + test_patterns: Some(svec!["a", "b"]), + worker_specifiers: None, + }, + ..Flags::default() + } + ); + + let r = flags_from_vec_safe(svec![ + "deno", + "cache", + "--worker", + "worker1.ts,worker2.ts" + ]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Cache { + files: svec![], + test_patterns: None, + worker_specifiers: Some(svec!["worker1.ts", "worker2.ts"]), }, ..Flags::default() } @@ -2414,6 +2493,8 @@ mod tests { Flags { subcommand: DenoSubcommand::Cache { files: svec!["script.ts"], + worker_specifiers: None, + test_patterns: None, }, unstable: true, import_map_path: Some("import_map.json".to_owned()), @@ -2456,6 +2537,8 @@ mod tests { Flags { subcommand: DenoSubcommand::Cache { files: svec!["script.ts", "script_two.ts"], + worker_specifiers: None, + test_patterns: None, }, ..Flags::default() } @@ -2979,6 +3062,8 @@ mod tests { Flags { subcommand: DenoSubcommand::Cache { files: svec!["script.ts", "script_two.ts"], + worker_specifiers: None, + test_patterns: None, }, ca_file: Some("example.crt".to_owned()), ..Flags::default() diff --git a/cli/main.rs b/cli/main.rs index 4469fe57c6..39fdd46549 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -94,6 +94,7 @@ use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; use upgrade::upgrade_command; +use worker::WebWorker; fn write_to_stdout_ignore_sigpipe(bytes: &[u8]) -> Result<(), std::io::Error> { use std::io::ErrorKind; @@ -236,6 +237,8 @@ async fn lint_command( async fn cache_command( flags: Flags, files: Vec, + worker_specifiers: Option>, + test_patterns: Option>, ) -> Result<(), AnyError> { let main_module = ModuleSpecifier::resolve_url_or_path("./$deno$cache.ts").unwrap(); @@ -249,6 +252,48 @@ async fn cache_command( worker.preload_module(&specifier).await.map(|_| ())?; } + if let Some(worker_specifiers) = worker_specifiers { + let mut web_worker = WebWorker::new( + "".to_string(), + Permissions::allow_all(), + main_module, + program_state.clone(), + true, + ); + for specifier in worker_specifiers { + let specifier = ModuleSpecifier::resolve_url_or_path(&specifier)?; + web_worker.preload_module(&specifier).await.map(|_| ())?; + } + } + + if let Some(test_patterns) = test_patterns { + let cwd = std::env::current_dir().expect("No current directory"); + let mut include = test_patterns; + if include.is_empty() { + include.push(".".to_string()); + } + let specifier = ModuleSpecifier::resolve_path("$deno$test.ts")?; + // Create a dummy source file. + let source_file = SourceFile { + filename: specifier.as_url().to_file_path().unwrap(), + url: specifier.as_url().clone(), + types_header: None, + media_type: MediaType::TypeScript, + source_code: test_runner::render_test_file( + test_runner::prepare_test_modules_urls(include, &cwd)?, + false, + false, + None, + ), + }; + // Save our fake file into file fetcher cache + // to allow module access by TS compiler + program_state + .file_fetcher + .save_source_file_in_cache(&specifier, source_file); + worker.preload_module(&specifier).await.map(|_| ())?; + } + Ok(()) } @@ -632,26 +677,19 @@ async fn test_command( } return Ok(()); } - - let test_file_path = cwd.join("$deno$test.ts"); - let test_file_url = - Url::from_file_path(&test_file_path).expect("Should be valid file url"); - let test_file = test_runner::render_test_file( - test_modules.clone(), - fail_fast, - quiet, - filter, - ); - let main_module = - ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); - let mut worker = MainWorker::new(&program_state, main_module.clone()); + let main_module = ModuleSpecifier::resolve_path("$deno$test.ts")?; // Create a dummy source file. let source_file = SourceFile { - filename: test_file_url.to_file_path().unwrap(), - url: test_file_url.clone(), + filename: main_module.as_url().to_file_path().unwrap(), + url: main_module.as_url().clone(), types_header: None, media_type: MediaType::TypeScript, - source_code: test_file.clone(), + source_code: test_runner::render_test_file( + test_modules.clone(), + fail_fast, + quiet, + filter, + ), }; // Save our fake file into file fetcher cache // to allow module access by TS compiler @@ -659,6 +697,8 @@ async fn test_command( .file_fetcher .save_source_file_in_cache(&main_module, source_file); + let mut worker = MainWorker::new(&program_state, main_module.clone()); + let mut maybe_coverage_collector = if flags.coverage { let session = worker.create_inspector_session(); let mut coverage_collector = CoverageCollector::new(session); @@ -680,8 +720,11 @@ async fn test_command( let coverages = coverage_collector.collect().await?; coverage_collector.stop_collecting().await?; - let filtered_coverages = - coverage::filter_script_coverages(coverages, test_file_url, test_modules); + let filtered_coverages = coverage::filter_script_coverages( + coverages, + main_module.as_url().clone(), + test_modules, + ); let mut coverage_reporter = PrettyCoverageReporter::new(quiet); for coverage in filtered_coverages { @@ -771,9 +814,12 @@ pub fn main() { code, as_typescript, } => eval_command(flags, code, as_typescript, print).boxed_local(), - DenoSubcommand::Cache { files } => { - cache_command(flags, files).boxed_local() - } + DenoSubcommand::Cache { + files, + worker_specifiers, + test_patterns, + } => cache_command(flags, files, worker_specifiers, test_patterns) + .boxed_local(), DenoSubcommand::Fmt { check, files, diff --git a/cli/tests/068_cache_test_type_error.out b/cli/tests/068_cache_test_type_error.out new file mode 100644 index 0000000000..f67ee66474 --- /dev/null +++ b/cli/tests/068_cache_test_type_error.out @@ -0,0 +1,4 @@ +[WILDCARD]error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. +const a: string = 1; + ^ + at [WILDCARD]foo_test.ts[WILDCARD] diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 11240e71bf..b448379b9b 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2137,6 +2137,18 @@ fn _066_prompt() { util::test_pty(args, output, input); } +itest!(_068_cache_test_type_error { + args: "cache --test=test_type_error", + output: "068_cache_test_type_error.out", + exit_code: 1, +}); + +itest!(_069_cache_worker { + args: "cache --worker subdir/worker_globals.ts", + output_str: Some("[WILDCARD]"), + exit_code: 0, +}); + itest!(_073_worker_error { args: "run -A 073_worker_error.ts", output: "073_worker_error.ts.out", diff --git a/cli/tests/test_type_error/foo_test.ts b/cli/tests/test_type_error/foo_test.ts new file mode 100644 index 0000000000..229e74798e --- /dev/null +++ b/cli/tests/test_type_error/foo_test.ts @@ -0,0 +1,2 @@ +/* eslint-disable */ +const a: string = 1; diff --git a/core/module_specifier.rs b/core/module_specifier.rs index 82452c0677..aec568d473 100644 --- a/core/module_specifier.rs +++ b/core/module_specifier.rs @@ -149,7 +149,7 @@ impl ModuleSpecifier { /// Converts a string representing a relative or absolute path into a /// ModuleSpecifier. A relative path is considered relative to the current /// working directory. - fn resolve_path( + pub fn resolve_path( path_str: &str, ) -> Result { let path = current_dir().unwrap().join(path_str);