diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e8c9e3bf99..2818a41a5a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,10 +10,6 @@ description = "Provides the deno executable" repository = "https://github.com/denoland/deno" default-run = "deno" -[lib] -name = "deno" -path = "lib.rs" - [[bin]] name = "deno" path = "main.rs" diff --git a/cli/lib.rs b/cli/lib.rs deleted file mode 100644 index 0b88d73460..0000000000 --- a/cli/lib.rs +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -#![deny(warnings)] - -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; -extern crate futures; -#[macro_use] -extern crate serde_json; -extern crate clap; -extern crate deno_core; -extern crate indexmap; -#[cfg(unix)] -extern crate nix; -extern crate rand; -extern crate regex; -extern crate reqwest; -extern crate serde; -extern crate serde_derive; -extern crate tokio; -extern crate url; - -mod checksum; -pub mod colors; -pub mod deno_dir; -pub mod diagnostics; -mod disk_cache; -mod doc; -mod file_fetcher; -pub mod flags; -mod fmt; -pub mod fmt_errors; -mod fs; -pub mod global_state; -mod global_timer; -pub mod http_cache; -mod http_util; -mod import_map; -mod inspector; -pub mod installer; -mod js; -mod lockfile; -mod metrics; -pub mod msg; -pub mod op_error; -pub mod ops; -pub mod permissions; -mod repl; -pub mod resolve_addr; -pub mod signal; -pub mod source_maps; -mod startup_data; -pub mod state; -mod swc_util; -mod test_runner; -pub mod test_util; -mod tokio_util; -mod tsc; -mod upgrade; -pub mod version; -mod web_worker; -pub mod worker; - -pub use dprint_plugin_typescript::swc_common; -pub use dprint_plugin_typescript::swc_ecma_ast; -pub use dprint_plugin_typescript::swc_ecma_parser; - -use crate::doc::parser::DocFileLoader; -use crate::file_fetcher::SourceFile; -use crate::file_fetcher::SourceFileFetcher; -use crate::global_state::GlobalState; -use crate::msg::MediaType; -use crate::op_error::OpError; -use crate::ops::io::get_stdio; -use crate::permissions::Permissions; -use crate::state::DebugType; -use crate::state::State; -use crate::tsc::TargetLib; -use crate::worker::MainWorker; -use deno_core::v8_set_flags; -use deno_core::ErrBox; -use deno_core::ModuleSpecifier; -use flags::DenoSubcommand; -use flags::Flags; -use futures::future::FutureExt; -use futures::Future; -use log::Level; -use log::Metadata; -use log::Record; -use std::env; -use std::io::Write; -use std::path::PathBuf; -use std::pin::Pin; -use upgrade::upgrade_command; -use url::Url; - -static LOGGER: Logger = Logger; - -// TODO(ry) Switch to env_logger or other standard crate. -struct Logger; - -impl log::Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= log::max_level() - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - let mut target = record.target().to_string(); - - if let Some(line_no) = record.line() { - target.push_str(":"); - target.push_str(&line_no.to_string()); - } - - if record.level() >= Level::Info { - eprintln!("{}", record.args()); - } else { - eprintln!("{} RS - {} - {}", record.level(), target, record.args()); - } - } - } - fn flush(&self) {} -} - -fn write_to_stdout_ignore_sigpipe(bytes: &[u8]) -> Result<(), std::io::Error> { - use std::io::ErrorKind; - - match std::io::stdout().write_all(bytes) { - Ok(()) => Ok(()), - Err(e) => match e.kind() { - ErrorKind::BrokenPipe => Ok(()), - _ => Err(e), - }, - } -} - -fn create_main_worker( - global_state: GlobalState, - main_module: ModuleSpecifier, -) -> Result { - let state = State::new(global_state, None, main_module, DebugType::Main)?; - - let mut worker = MainWorker::new( - "main".to_string(), - startup_data::deno_isolate_init(), - state, - ); - - { - let (stdin, stdout, stderr) = get_stdio(); - let mut t = worker.resource_table.borrow_mut(); - t.add("stdin", Box::new(stdin)); - t.add("stdout", Box::new(stdout)); - t.add("stderr", Box::new(stderr)); - } - - worker.execute("bootstrap.mainRuntime()")?; - Ok(worker) -} - -fn print_cache_info(state: &GlobalState) { - println!( - "{} {:?}", - colors::bold("DENO_DIR location:".to_string()), - state.dir.root - ); - println!( - "{} {:?}", - colors::bold("Remote modules cache:".to_string()), - state.file_fetcher.http_cache.location - ); - println!( - "{} {:?}", - colors::bold("TypeScript compiler cache:".to_string()), - state.dir.gen_cache.location - ); -} - -// TODO(bartlomieju): this function de facto repeats -// whole compilation stack. Can this be done better somehow? -async fn print_file_info( - worker: &MainWorker, - module_specifier: ModuleSpecifier, -) -> Result<(), ErrBox> { - let global_state = worker.state.borrow().global_state.clone(); - - let out = global_state - .file_fetcher - .fetch_source_file(&module_specifier, None, Permissions::allow_all()) - .await?; - - println!( - "{} {}", - colors::bold("local:".to_string()), - out.filename.to_str().unwrap() - ); - - println!( - "{} {}", - colors::bold("type:".to_string()), - msg::enum_name_media_type(out.media_type) - ); - - let module_specifier_ = module_specifier.clone(); - global_state - .clone() - .fetch_compiled_module( - module_specifier_, - None, - TargetLib::Main, - Permissions::allow_all(), - ) - .await?; - - if out.media_type == msg::MediaType::TypeScript - || (out.media_type == msg::MediaType::JavaScript - && global_state.ts_compiler.compile_js) - { - let compiled_source_file = global_state - .ts_compiler - .get_compiled_source_file(&out.url) - .unwrap(); - - println!( - "{} {}", - colors::bold("compiled:".to_string()), - compiled_source_file.filename.to_str().unwrap(), - ); - } - - if let Ok(source_map) = global_state - .clone() - .ts_compiler - .get_source_map_file(&module_specifier) - { - println!( - "{} {}", - colors::bold("map:".to_string()), - source_map.filename.to_str().unwrap() - ); - } - - if let Some(deps) = worker.isolate.modules.deps(&module_specifier) { - println!("{}{}", colors::bold("deps:\n".to_string()), deps.name); - if let Some(ref depsdeps) = deps.deps { - for d in depsdeps { - println!("{}", d); - } - } - } else { - println!( - "{} cannot retrieve full dependency graph", - colors::bold("deps:".to_string()), - ); - } - - Ok(()) -} - -fn get_types(unstable: bool) -> String { - if unstable { - format!( - "{}\n{}\n{}\n{}", - crate::js::DENO_NS_LIB, - crate::js::SHARED_GLOBALS_LIB, - crate::js::WINDOW_LIB, - crate::js::UNSTABLE_NS_LIB, - ) - } else { - format!( - "{}\n{}\n{}", - crate::js::DENO_NS_LIB, - crate::js::SHARED_GLOBALS_LIB, - crate::js::WINDOW_LIB, - ) - } -} - -async fn info_command( - flags: Flags, - file: Option, -) -> Result<(), ErrBox> { - let global_state = GlobalState::new(flags)?; - // If it was just "deno info" print location of caches and exit - if file.is_none() { - print_cache_info(&global_state); - return Ok(()); - } - - let main_module = ModuleSpecifier::resolve_url_or_path(&file.unwrap())?; - let mut worker = create_main_worker(global_state, main_module.clone())?; - worker.preload_module(&main_module).await?; - print_file_info(&worker, main_module.clone()).await -} - -async fn install_command( - flags: Flags, - module_url: String, - args: Vec, - name: Option, - root: Option, - force: bool, -) -> Result<(), ErrBox> { - // Firstly fetch and compile module, this step ensures that module exists. - let mut fetch_flags = flags.clone(); - fetch_flags.reload = true; - let global_state = GlobalState::new(fetch_flags)?; - let main_module = ModuleSpecifier::resolve_url_or_path(&module_url)?; - let mut worker = create_main_worker(global_state, main_module.clone())?; - worker.preload_module(&main_module).await?; - installer::install(flags, &module_url, args, name, root, force) - .map_err(ErrBox::from) -} - -async fn cache_command(flags: Flags, files: Vec) -> Result<(), ErrBox> { - let main_module = - ModuleSpecifier::resolve_url_or_path("./__$deno$fetch.ts").unwrap(); - let global_state = GlobalState::new(flags)?; - let mut worker = - create_main_worker(global_state.clone(), main_module.clone())?; - - for file in files { - let specifier = ModuleSpecifier::resolve_url_or_path(&file)?; - worker.preload_module(&specifier).await.map(|_| ())?; - } - - if global_state.flags.lock_write { - if let Some(ref lockfile) = global_state.lockfile { - let g = lockfile.lock().unwrap(); - g.write()?; - } else { - eprintln!("--lock flag must be specified when using --lock-write"); - std::process::exit(11); - } - } - - Ok(()) -} - -async fn eval_command( - flags: Flags, - code: String, - as_typescript: bool, -) -> Result<(), ErrBox> { - // Force TypeScript compile. - let main_module = - ModuleSpecifier::resolve_url_or_path("./__$deno$eval.ts").unwrap(); - let global_state = GlobalState::new(flags)?; - let mut worker = create_main_worker(global_state, main_module.clone())?; - let main_module_url = main_module.as_url().to_owned(); - // Create a dummy source file. - let source_file = SourceFile { - filename: main_module_url.to_file_path().unwrap(), - url: main_module_url, - types_url: None, - media_type: if as_typescript { - MediaType::TypeScript - } else { - MediaType::JavaScript - }, - source_code: code.clone().into_bytes(), - }; - // Save our fake file into file fetcher cache - // to allow module access by TS compiler (e.g. op_fetch_source_files) - worker - .state - .borrow() - .global_state - .file_fetcher - .save_source_file_in_cache(&main_module, source_file); - debug!("main_module {}", &main_module); - worker.execute_module(&main_module).await?; - worker.execute("window.dispatchEvent(new Event('load'))")?; - (&mut *worker).await?; - worker.execute("window.dispatchEvent(new Event('unload'))")?; - Ok(()) -} - -async fn bundle_command( - flags: Flags, - source_file: String, - out_file: Option, -) -> Result<(), ErrBox> { - let module_name = ModuleSpecifier::resolve_url_or_path(&source_file)?; - let global_state = GlobalState::new(flags)?; - debug!(">>>>> bundle START"); - let bundle_result = global_state - .ts_compiler - .bundle(global_state.clone(), module_name.to_string(), out_file) - .await; - debug!(">>>>> bundle END"); - bundle_result -} - -async fn doc_command( - flags: Flags, - source_file: Option, - json: bool, - maybe_filter: Option, -) -> Result<(), ErrBox> { - let global_state = GlobalState::new(flags.clone())?; - let source_file = source_file.unwrap_or_else(|| "--builtin".to_string()); - - impl DocFileLoader for SourceFileFetcher { - fn load_source_code( - &self, - specifier: &str, - ) -> Pin>>> { - let specifier = - ModuleSpecifier::resolve_url_or_path(specifier).expect("Bad specifier"); - let fetcher = self.clone(); - - async move { - let source_file = fetcher - .fetch_source_file(&specifier, None, Permissions::allow_all()) - .await?; - String::from_utf8(source_file.source_code) - .map_err(|_| OpError::other("failed to parse".to_string())) - } - .boxed_local() - } - } - - let loader = Box::new(global_state.file_fetcher.clone()); - let doc_parser = doc::DocParser::new(loader); - - let parse_result = if source_file == "--builtin" { - doc_parser.parse_source("lib.deno.d.ts", get_types(flags.unstable).as_str()) - } else { - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&source_file).unwrap(); - doc_parser - .parse_with_reexports(&module_specifier.to_string()) - .await - }; - - let doc_nodes = match parse_result { - Ok(nodes) => nodes, - Err(e) => { - eprintln!("{}", e); - std::process::exit(1); - } - }; - - if json { - let writer = std::io::BufWriter::new(std::io::stdout()); - serde_json::to_writer_pretty(writer, &doc_nodes).map_err(ErrBox::from) - } else { - let details = if let Some(filter) = maybe_filter { - let node = doc::find_node_by_name_recursively(doc_nodes, filter.clone()); - if let Some(node) = node { - doc::printer::format_details(node) - } else { - eprintln!("Node {} was not found!", filter); - std::process::exit(1); - } - } else { - doc::printer::format(doc_nodes) - }; - - write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(ErrBox::from) - } -} - -async fn run_repl(flags: Flags) -> Result<(), ErrBox> { - let main_module = - ModuleSpecifier::resolve_url_or_path("./__$deno$repl.ts").unwrap(); - let global_state = GlobalState::new(flags)?; - let mut worker = create_main_worker(global_state, main_module)?; - loop { - (&mut *worker).await?; - } -} - -async fn run_command(flags: Flags, script: String) -> Result<(), ErrBox> { - let global_state = GlobalState::new(flags.clone())?; - let main_module = ModuleSpecifier::resolve_url_or_path(&script).unwrap(); - let mut worker = - create_main_worker(global_state.clone(), main_module.clone())?; - debug!("main_module {}", main_module); - worker.execute_module(&main_module).await?; - worker.execute("window.dispatchEvent(new Event('load'))")?; - (&mut *worker).await?; - worker.execute("window.dispatchEvent(new Event('unload'))")?; - if global_state.flags.lock_write { - if let Some(ref lockfile) = global_state.lockfile { - let g = lockfile.lock().unwrap(); - g.write()?; - } else { - eprintln!("--lock flag must be specified when using --lock-write"); - std::process::exit(11); - } - } - Ok(()) -} - -async fn test_command( - flags: Flags, - include: Option>, - fail_fast: bool, - quiet: bool, - allow_none: bool, - filter: Option, -) -> Result<(), ErrBox> { - let global_state = GlobalState::new(flags.clone())?; - let cwd = std::env::current_dir().expect("No current directory"); - let include = include.unwrap_or_else(|| vec![".".to_string()]); - let test_modules = test_runner::prepare_test_modules_urls(include, &cwd)?; - - if test_modules.is_empty() { - println!("No matching test modules found"); - if !allow_none { - std::process::exit(1); - } - 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, fail_fast, quiet, filter); - let main_module = - ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); - let mut worker = - create_main_worker(global_state.clone(), main_module.clone())?; - // Create a dummy source file. - let source_file = SourceFile { - filename: test_file_url.to_file_path().unwrap(), - url: test_file_url, - types_url: None, - media_type: MediaType::TypeScript, - source_code: test_file.clone().into_bytes(), - }; - // Save our fake file into file fetcher cache - // to allow module access by TS compiler (e.g. op_fetch_source_files) - worker - .state - .borrow() - .global_state - .file_fetcher - .save_source_file_in_cache(&main_module, source_file); - let execute_result = worker.execute_module(&main_module).await; - execute_result?; - worker.execute("window.dispatchEvent(new Event('load'))")?; - (&mut *worker).await?; - worker.execute("window.dispatchEvent(new Event('unload'))") -} - -pub fn main() { - #[cfg(windows)] - colors::enable_ansi(); // For Windows 10 - - log::set_logger(&LOGGER).unwrap(); - let args: Vec = env::args().collect(); - let flags = flags::flags_from_vec(args); - - if let Some(ref v8_flags) = flags.v8_flags { - let mut v8_flags_ = v8_flags.clone(); - v8_flags_.insert(0, "UNUSED_BUT_NECESSARY_ARG0".to_string()); - v8_set_flags(v8_flags_); - } - - let log_level = match flags.log_level { - Some(level) => level, - None => Level::Info, // Default log level - }; - log::set_max_level(log_level.to_level_filter()); - - let fut = match flags.clone().subcommand { - DenoSubcommand::Bundle { - source_file, - out_file, - } => bundle_command(flags, source_file, out_file).boxed_local(), - DenoSubcommand::Doc { - source_file, - json, - filter, - } => doc_command(flags, source_file, json, filter).boxed_local(), - DenoSubcommand::Eval { - code, - as_typescript, - } => eval_command(flags, code, as_typescript).boxed_local(), - DenoSubcommand::Cache { files } => { - cache_command(flags, files).boxed_local() - } - DenoSubcommand::Fmt { check, files } => { - fmt::format(files, check).boxed_local() - } - DenoSubcommand::Info { file } => info_command(flags, file).boxed_local(), - DenoSubcommand::Install { - module_url, - args, - name, - root, - force, - } => { - install_command(flags, module_url, args, name, root, force).boxed_local() - } - DenoSubcommand::Repl => run_repl(flags).boxed_local(), - DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(), - DenoSubcommand::Test { - fail_fast, - quiet, - include, - allow_none, - filter, - } => test_command(flags, include, fail_fast, quiet, allow_none, filter) - .boxed_local(), - DenoSubcommand::Completions { buf } => { - if let Err(e) = write_to_stdout_ignore_sigpipe(&buf) { - eprintln!("{}", e); - std::process::exit(1); - } - return; - } - DenoSubcommand::Types => { - let types = get_types(flags.unstable); - if let Err(e) = write_to_stdout_ignore_sigpipe(types.as_bytes()) { - eprintln!("{}", e); - std::process::exit(1); - } - return; - } - DenoSubcommand::Upgrade { - force, - dry_run, - version, - } => upgrade_command(dry_run, force, version).boxed_local(), - _ => unreachable!(), - }; - - let result = tokio_util::run_basic(fut); - if let Err(err) = result { - let msg = format!( - "{}: {}", - colors::red_bold("error".to_string()), - err.to_string(), - ); - eprintln!("{}", msg); - std::process::exit(1); - } -} diff --git a/cli/main.rs b/cli/main.rs index 40f953e7a5..0b88d73460 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -1,5 +1,645 @@ -extern crate deno; +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +#![deny(warnings)] -fn main() { - deno::main(); +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; +extern crate futures; +#[macro_use] +extern crate serde_json; +extern crate clap; +extern crate deno_core; +extern crate indexmap; +#[cfg(unix)] +extern crate nix; +extern crate rand; +extern crate regex; +extern crate reqwest; +extern crate serde; +extern crate serde_derive; +extern crate tokio; +extern crate url; + +mod checksum; +pub mod colors; +pub mod deno_dir; +pub mod diagnostics; +mod disk_cache; +mod doc; +mod file_fetcher; +pub mod flags; +mod fmt; +pub mod fmt_errors; +mod fs; +pub mod global_state; +mod global_timer; +pub mod http_cache; +mod http_util; +mod import_map; +mod inspector; +pub mod installer; +mod js; +mod lockfile; +mod metrics; +pub mod msg; +pub mod op_error; +pub mod ops; +pub mod permissions; +mod repl; +pub mod resolve_addr; +pub mod signal; +pub mod source_maps; +mod startup_data; +pub mod state; +mod swc_util; +mod test_runner; +pub mod test_util; +mod tokio_util; +mod tsc; +mod upgrade; +pub mod version; +mod web_worker; +pub mod worker; + +pub use dprint_plugin_typescript::swc_common; +pub use dprint_plugin_typescript::swc_ecma_ast; +pub use dprint_plugin_typescript::swc_ecma_parser; + +use crate::doc::parser::DocFileLoader; +use crate::file_fetcher::SourceFile; +use crate::file_fetcher::SourceFileFetcher; +use crate::global_state::GlobalState; +use crate::msg::MediaType; +use crate::op_error::OpError; +use crate::ops::io::get_stdio; +use crate::permissions::Permissions; +use crate::state::DebugType; +use crate::state::State; +use crate::tsc::TargetLib; +use crate::worker::MainWorker; +use deno_core::v8_set_flags; +use deno_core::ErrBox; +use deno_core::ModuleSpecifier; +use flags::DenoSubcommand; +use flags::Flags; +use futures::future::FutureExt; +use futures::Future; +use log::Level; +use log::Metadata; +use log::Record; +use std::env; +use std::io::Write; +use std::path::PathBuf; +use std::pin::Pin; +use upgrade::upgrade_command; +use url::Url; + +static LOGGER: Logger = Logger; + +// TODO(ry) Switch to env_logger or other standard crate. +struct Logger; + +impl log::Log for Logger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let mut target = record.target().to_string(); + + if let Some(line_no) = record.line() { + target.push_str(":"); + target.push_str(&line_no.to_string()); + } + + if record.level() >= Level::Info { + eprintln!("{}", record.args()); + } else { + eprintln!("{} RS - {} - {}", record.level(), target, record.args()); + } + } + } + fn flush(&self) {} +} + +fn write_to_stdout_ignore_sigpipe(bytes: &[u8]) -> Result<(), std::io::Error> { + use std::io::ErrorKind; + + match std::io::stdout().write_all(bytes) { + Ok(()) => Ok(()), + Err(e) => match e.kind() { + ErrorKind::BrokenPipe => Ok(()), + _ => Err(e), + }, + } +} + +fn create_main_worker( + global_state: GlobalState, + main_module: ModuleSpecifier, +) -> Result { + let state = State::new(global_state, None, main_module, DebugType::Main)?; + + let mut worker = MainWorker::new( + "main".to_string(), + startup_data::deno_isolate_init(), + state, + ); + + { + let (stdin, stdout, stderr) = get_stdio(); + let mut t = worker.resource_table.borrow_mut(); + t.add("stdin", Box::new(stdin)); + t.add("stdout", Box::new(stdout)); + t.add("stderr", Box::new(stderr)); + } + + worker.execute("bootstrap.mainRuntime()")?; + Ok(worker) +} + +fn print_cache_info(state: &GlobalState) { + println!( + "{} {:?}", + colors::bold("DENO_DIR location:".to_string()), + state.dir.root + ); + println!( + "{} {:?}", + colors::bold("Remote modules cache:".to_string()), + state.file_fetcher.http_cache.location + ); + println!( + "{} {:?}", + colors::bold("TypeScript compiler cache:".to_string()), + state.dir.gen_cache.location + ); +} + +// TODO(bartlomieju): this function de facto repeats +// whole compilation stack. Can this be done better somehow? +async fn print_file_info( + worker: &MainWorker, + module_specifier: ModuleSpecifier, +) -> Result<(), ErrBox> { + let global_state = worker.state.borrow().global_state.clone(); + + let out = global_state + .file_fetcher + .fetch_source_file(&module_specifier, None, Permissions::allow_all()) + .await?; + + println!( + "{} {}", + colors::bold("local:".to_string()), + out.filename.to_str().unwrap() + ); + + println!( + "{} {}", + colors::bold("type:".to_string()), + msg::enum_name_media_type(out.media_type) + ); + + let module_specifier_ = module_specifier.clone(); + global_state + .clone() + .fetch_compiled_module( + module_specifier_, + None, + TargetLib::Main, + Permissions::allow_all(), + ) + .await?; + + if out.media_type == msg::MediaType::TypeScript + || (out.media_type == msg::MediaType::JavaScript + && global_state.ts_compiler.compile_js) + { + let compiled_source_file = global_state + .ts_compiler + .get_compiled_source_file(&out.url) + .unwrap(); + + println!( + "{} {}", + colors::bold("compiled:".to_string()), + compiled_source_file.filename.to_str().unwrap(), + ); + } + + if let Ok(source_map) = global_state + .clone() + .ts_compiler + .get_source_map_file(&module_specifier) + { + println!( + "{} {}", + colors::bold("map:".to_string()), + source_map.filename.to_str().unwrap() + ); + } + + if let Some(deps) = worker.isolate.modules.deps(&module_specifier) { + println!("{}{}", colors::bold("deps:\n".to_string()), deps.name); + if let Some(ref depsdeps) = deps.deps { + for d in depsdeps { + println!("{}", d); + } + } + } else { + println!( + "{} cannot retrieve full dependency graph", + colors::bold("deps:".to_string()), + ); + } + + Ok(()) +} + +fn get_types(unstable: bool) -> String { + if unstable { + format!( + "{}\n{}\n{}\n{}", + crate::js::DENO_NS_LIB, + crate::js::SHARED_GLOBALS_LIB, + crate::js::WINDOW_LIB, + crate::js::UNSTABLE_NS_LIB, + ) + } else { + format!( + "{}\n{}\n{}", + crate::js::DENO_NS_LIB, + crate::js::SHARED_GLOBALS_LIB, + crate::js::WINDOW_LIB, + ) + } +} + +async fn info_command( + flags: Flags, + file: Option, +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags)?; + // If it was just "deno info" print location of caches and exit + if file.is_none() { + print_cache_info(&global_state); + return Ok(()); + } + + let main_module = ModuleSpecifier::resolve_url_or_path(&file.unwrap())?; + let mut worker = create_main_worker(global_state, main_module.clone())?; + worker.preload_module(&main_module).await?; + print_file_info(&worker, main_module.clone()).await +} + +async fn install_command( + flags: Flags, + module_url: String, + args: Vec, + name: Option, + root: Option, + force: bool, +) -> Result<(), ErrBox> { + // Firstly fetch and compile module, this step ensures that module exists. + let mut fetch_flags = flags.clone(); + fetch_flags.reload = true; + let global_state = GlobalState::new(fetch_flags)?; + let main_module = ModuleSpecifier::resolve_url_or_path(&module_url)?; + let mut worker = create_main_worker(global_state, main_module.clone())?; + worker.preload_module(&main_module).await?; + installer::install(flags, &module_url, args, name, root, force) + .map_err(ErrBox::from) +} + +async fn cache_command(flags: Flags, files: Vec) -> Result<(), ErrBox> { + let main_module = + ModuleSpecifier::resolve_url_or_path("./__$deno$fetch.ts").unwrap(); + let global_state = GlobalState::new(flags)?; + let mut worker = + create_main_worker(global_state.clone(), main_module.clone())?; + + for file in files { + let specifier = ModuleSpecifier::resolve_url_or_path(&file)?; + worker.preload_module(&specifier).await.map(|_| ())?; + } + + if global_state.flags.lock_write { + if let Some(ref lockfile) = global_state.lockfile { + let g = lockfile.lock().unwrap(); + g.write()?; + } else { + eprintln!("--lock flag must be specified when using --lock-write"); + std::process::exit(11); + } + } + + Ok(()) +} + +async fn eval_command( + flags: Flags, + code: String, + as_typescript: bool, +) -> Result<(), ErrBox> { + // Force TypeScript compile. + let main_module = + ModuleSpecifier::resolve_url_or_path("./__$deno$eval.ts").unwrap(); + let global_state = GlobalState::new(flags)?; + let mut worker = create_main_worker(global_state, main_module.clone())?; + let main_module_url = main_module.as_url().to_owned(); + // Create a dummy source file. + let source_file = SourceFile { + filename: main_module_url.to_file_path().unwrap(), + url: main_module_url, + types_url: None, + media_type: if as_typescript { + MediaType::TypeScript + } else { + MediaType::JavaScript + }, + source_code: code.clone().into_bytes(), + }; + // Save our fake file into file fetcher cache + // to allow module access by TS compiler (e.g. op_fetch_source_files) + worker + .state + .borrow() + .global_state + .file_fetcher + .save_source_file_in_cache(&main_module, source_file); + debug!("main_module {}", &main_module); + worker.execute_module(&main_module).await?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))")?; + Ok(()) +} + +async fn bundle_command( + flags: Flags, + source_file: String, + out_file: Option, +) -> Result<(), ErrBox> { + let module_name = ModuleSpecifier::resolve_url_or_path(&source_file)?; + let global_state = GlobalState::new(flags)?; + debug!(">>>>> bundle START"); + let bundle_result = global_state + .ts_compiler + .bundle(global_state.clone(), module_name.to_string(), out_file) + .await; + debug!(">>>>> bundle END"); + bundle_result +} + +async fn doc_command( + flags: Flags, + source_file: Option, + json: bool, + maybe_filter: Option, +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; + let source_file = source_file.unwrap_or_else(|| "--builtin".to_string()); + + impl DocFileLoader for SourceFileFetcher { + fn load_source_code( + &self, + specifier: &str, + ) -> Pin>>> { + let specifier = + ModuleSpecifier::resolve_url_or_path(specifier).expect("Bad specifier"); + let fetcher = self.clone(); + + async move { + let source_file = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await?; + String::from_utf8(source_file.source_code) + .map_err(|_| OpError::other("failed to parse".to_string())) + } + .boxed_local() + } + } + + let loader = Box::new(global_state.file_fetcher.clone()); + let doc_parser = doc::DocParser::new(loader); + + let parse_result = if source_file == "--builtin" { + doc_parser.parse_source("lib.deno.d.ts", get_types(flags.unstable).as_str()) + } else { + let module_specifier = + ModuleSpecifier::resolve_url_or_path(&source_file).unwrap(); + doc_parser + .parse_with_reexports(&module_specifier.to_string()) + .await + }; + + let doc_nodes = match parse_result { + Ok(nodes) => nodes, + Err(e) => { + eprintln!("{}", e); + std::process::exit(1); + } + }; + + if json { + let writer = std::io::BufWriter::new(std::io::stdout()); + serde_json::to_writer_pretty(writer, &doc_nodes).map_err(ErrBox::from) + } else { + let details = if let Some(filter) = maybe_filter { + let node = doc::find_node_by_name_recursively(doc_nodes, filter.clone()); + if let Some(node) = node { + doc::printer::format_details(node) + } else { + eprintln!("Node {} was not found!", filter); + std::process::exit(1); + } + } else { + doc::printer::format(doc_nodes) + }; + + write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(ErrBox::from) + } +} + +async fn run_repl(flags: Flags) -> Result<(), ErrBox> { + let main_module = + ModuleSpecifier::resolve_url_or_path("./__$deno$repl.ts").unwrap(); + let global_state = GlobalState::new(flags)?; + let mut worker = create_main_worker(global_state, main_module)?; + loop { + (&mut *worker).await?; + } +} + +async fn run_command(flags: Flags, script: String) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; + let main_module = ModuleSpecifier::resolve_url_or_path(&script).unwrap(); + let mut worker = + create_main_worker(global_state.clone(), main_module.clone())?; + debug!("main_module {}", main_module); + worker.execute_module(&main_module).await?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))")?; + if global_state.flags.lock_write { + if let Some(ref lockfile) = global_state.lockfile { + let g = lockfile.lock().unwrap(); + g.write()?; + } else { + eprintln!("--lock flag must be specified when using --lock-write"); + std::process::exit(11); + } + } + Ok(()) +} + +async fn test_command( + flags: Flags, + include: Option>, + fail_fast: bool, + quiet: bool, + allow_none: bool, + filter: Option, +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; + let cwd = std::env::current_dir().expect("No current directory"); + let include = include.unwrap_or_else(|| vec![".".to_string()]); + let test_modules = test_runner::prepare_test_modules_urls(include, &cwd)?; + + if test_modules.is_empty() { + println!("No matching test modules found"); + if !allow_none { + std::process::exit(1); + } + 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, fail_fast, quiet, filter); + let main_module = + ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); + let mut worker = + create_main_worker(global_state.clone(), main_module.clone())?; + // Create a dummy source file. + let source_file = SourceFile { + filename: test_file_url.to_file_path().unwrap(), + url: test_file_url, + types_url: None, + media_type: MediaType::TypeScript, + source_code: test_file.clone().into_bytes(), + }; + // Save our fake file into file fetcher cache + // to allow module access by TS compiler (e.g. op_fetch_source_files) + worker + .state + .borrow() + .global_state + .file_fetcher + .save_source_file_in_cache(&main_module, source_file); + let execute_result = worker.execute_module(&main_module).await; + execute_result?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))") +} + +pub fn main() { + #[cfg(windows)] + colors::enable_ansi(); // For Windows 10 + + log::set_logger(&LOGGER).unwrap(); + let args: Vec = env::args().collect(); + let flags = flags::flags_from_vec(args); + + if let Some(ref v8_flags) = flags.v8_flags { + let mut v8_flags_ = v8_flags.clone(); + v8_flags_.insert(0, "UNUSED_BUT_NECESSARY_ARG0".to_string()); + v8_set_flags(v8_flags_); + } + + let log_level = match flags.log_level { + Some(level) => level, + None => Level::Info, // Default log level + }; + log::set_max_level(log_level.to_level_filter()); + + let fut = match flags.clone().subcommand { + DenoSubcommand::Bundle { + source_file, + out_file, + } => bundle_command(flags, source_file, out_file).boxed_local(), + DenoSubcommand::Doc { + source_file, + json, + filter, + } => doc_command(flags, source_file, json, filter).boxed_local(), + DenoSubcommand::Eval { + code, + as_typescript, + } => eval_command(flags, code, as_typescript).boxed_local(), + DenoSubcommand::Cache { files } => { + cache_command(flags, files).boxed_local() + } + DenoSubcommand::Fmt { check, files } => { + fmt::format(files, check).boxed_local() + } + DenoSubcommand::Info { file } => info_command(flags, file).boxed_local(), + DenoSubcommand::Install { + module_url, + args, + name, + root, + force, + } => { + install_command(flags, module_url, args, name, root, force).boxed_local() + } + DenoSubcommand::Repl => run_repl(flags).boxed_local(), + DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(), + DenoSubcommand::Test { + fail_fast, + quiet, + include, + allow_none, + filter, + } => test_command(flags, include, fail_fast, quiet, allow_none, filter) + .boxed_local(), + DenoSubcommand::Completions { buf } => { + if let Err(e) = write_to_stdout_ignore_sigpipe(&buf) { + eprintln!("{}", e); + std::process::exit(1); + } + return; + } + DenoSubcommand::Types => { + let types = get_types(flags.unstable); + if let Err(e) = write_to_stdout_ignore_sigpipe(types.as_bytes()) { + eprintln!("{}", e); + std::process::exit(1); + } + return; + } + DenoSubcommand::Upgrade { + force, + dry_run, + version, + } => upgrade_command(dry_run, force, version).boxed_local(), + _ => unreachable!(), + }; + + let result = tokio_util::run_basic(fut); + if let Err(err) = result { + let msg = format!( + "{}: {}", + colors::red_bold("error".to_string()), + err.to_string(), + ); + eprintln!("{}", msg); + std::process::exit(1); + } } diff --git a/cli/test_util.rs b/cli/test_util.rs index 890b2783fd..6d8c58db5c 100644 --- a/cli/test_util.rs +++ b/cli/test_util.rs @@ -1,9 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -// TODO(ry) Make this file test-only. Somehow it's very difficult to export -// methods to tests/integration_tests.rs if this is enabled... -// #![cfg(test)] - +#![cfg(test)] use std::path::PathBuf; use std::process::Child; use std::process::Command; diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 7437b17447..8c31c14080 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -15,21 +15,19 @@ use tempfile::TempDir; #[test] fn std_tests() { let dir = TempDir::new().expect("tempdir fail"); - let mut deno_cmd = Command::new(util::deno_exe_path()); - deno_cmd.env("DENO_DIR", dir.path()); - - let mut cwd = util::root_path(); - cwd.push("std"); - let mut deno = deno_cmd - .current_dir(cwd) // note: std tests expect to run from "std" dir + let std_path = util::root_path().join("std"); + let status = util::deno_cmd() + .env("DENO_DIR", dir.path()) + .current_dir(std_path) // TODO(ry) change this to root_path .arg("test") .arg("--unstable") .arg("--seed=86") // Some tests rely on specific random numbers. .arg("-A") // .arg("-Ldebug") .spawn() - .expect("failed to spawn script"); - let status = deno.wait().expect("failed to wait for the child process"); + .unwrap() + .wait() + .unwrap(); assert!(status.success()); } @@ -51,7 +49,7 @@ fn x_deno_warning() { let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim(); let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim(); assert_eq!("testing x-deno-warning header", stdout_str); - assert!(deno::colors::strip_ansi_codes(stderr_str).contains("Warning foobar")); + assert!(util::strip_ansi_codes(stderr_str).contains("Warning foobar")); drop(g); } @@ -110,7 +108,6 @@ pub fn test_raw_tty() { nread = master.read(&mut obytes).unwrap(); assert_eq!(String::from_utf8_lossy(&obytes[0..nread]), "C"); } else { - use deno::test_util::*; use nix::sys::termios; use std::os::unix::io::AsRawFd; use std::process::*; @@ -122,7 +119,7 @@ pub fn test_raw_tty() { termios::tcsetattr(stdin_fd, termios::SetArg::TCSANOW, &t).unwrap(); let deno_dir = TempDir::new().expect("tempdir fail"); - let mut child = Command::new(deno_exe_path()) + let mut child = Command::new(util::deno_exe_path()) .env("DENO_DIR", deno_dir.path()) .current_dir(util::root_path()) .arg("run") @@ -292,23 +289,25 @@ fn installer_test_local_module_run() { let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); - let local_module = std::env::current_dir().unwrap().join("tests/echo.ts"); - let local_module_str = local_module.to_string_lossy(); - deno::installer::install( - deno::flags::Flags::default(), - &local_module_str, - vec!["hello".to_string()], - Some("echo_test".to_string()), - Some(temp_dir.path().to_path_buf()), - false, - ) - .expect("Failed to install"); + let status = util::deno_cmd() + .current_dir(util::root_path()) + .arg("install") + .arg("--name") + .arg("echo_test") + .arg("--root") + .arg(temp_dir.path()) + .arg(util::tests_path().join("echo.ts")) + .arg("hello") + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(status.success()); let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); } assert!(file_path.exists()); - // NOTE: using file_path here instead of exec_name, because tests // shouldn't mess with user's PATH env variable let output = Command::new(file_path) @@ -317,13 +316,8 @@ fn installer_test_local_module_run() { .env("PATH", util::target_dir()) .output() .expect("failed to spawn script"); - let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim(); - let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim(); - println!("Got stdout: {:?}", stdout_str); - println!("Got stderr: {:?}", stderr_str); assert!(stdout_str.ends_with("hello, foo")); - drop(temp_dir); } #[test] @@ -332,15 +326,20 @@ fn installer_test_remote_module_run() { let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); - deno::installer::install( - deno::flags::Flags::default(), - "http://localhost:4545/cli/tests/echo.ts", - vec!["hello".to_string()], - Some("echo_test".to_string()), - Some(temp_dir.path().to_path_buf()), - false, - ) - .expect("Failed to install"); + let status = util::deno_cmd() + .current_dir(util::root_path()) + .arg("install") + .arg("--name") + .arg("echo_test") + .arg("--root") + .arg(temp_dir.path()) + .arg("http://localhost:4545/cli/tests/echo.ts") + .arg("hello") + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(status.success()); let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); @@ -356,7 +355,6 @@ fn installer_test_remote_module_run() { .unwrap() .trim() .ends_with("hello, foo")); - drop(temp_dir); drop(g) } @@ -1730,8 +1728,6 @@ fn cafile_fetch() { #[test] fn cafile_install_remote_module() { - use deno::test_util::*; - let g = util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); @@ -1739,7 +1735,7 @@ fn cafile_install_remote_module() { let deno_dir = TempDir::new().expect("tempdir fail"); let cafile = util::root_path().join("cli/tests/tls/RootCA.pem"); - let install_output = Command::new(deno_exe_path()) + let install_output = Command::new(util::deno_exe_path()) .env("DENO_DIR", deno_dir.path()) .current_dir(util::root_path()) .arg("install") @@ -2471,20 +2467,22 @@ fn exec_path() { let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim(); let actual = std::fs::canonicalize(&std::path::Path::new(stdout_str)).unwrap(); - let expected = - std::fs::canonicalize(deno::test_util::deno_exe_path()).unwrap(); + let expected = std::fs::canonicalize(util::deno_exe_path()).unwrap(); assert_eq!(expected, actual); } mod util { - use deno::colors::strip_ansi_codes; - pub use deno::test_util::*; use os_pipe::pipe; + use regex::Regex; use std::io::Read; use std::io::Write; + use std::path::PathBuf; + use std::process::Child; use std::process::Command; use std::process::Output; use std::process::Stdio; + use std::sync::Mutex; + use std::sync::MutexGuard; use tempfile::TempDir; pub const PERMISSION_VARIANTS: [&str; 5] = @@ -2493,6 +2491,94 @@ mod util { lazy_static! { static ref DENO_DIR: TempDir = { TempDir::new().expect("tempdir fail") }; + + // STRIP_ANSI_RE and strip_ansi_codes are lifted from the "console" crate. + // Copyright 2017 Armin Ronacher . MIT License. + static ref STRIP_ANSI_RE: Regex = Regex::new( + r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" + ).unwrap(); + + static ref GUARD: Mutex<()> = Mutex::new(()); + } + + pub fn root_path() -> PathBuf { + PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/..")) + } + + pub fn tests_path() -> PathBuf { + root_path().join("cli").join("tests") + } + + pub fn target_dir() -> PathBuf { + let current_exe = std::env::current_exe().unwrap(); + let target_dir = current_exe.parent().unwrap().parent().unwrap(); + println!("target_dir {}", target_dir.display()); + target_dir.into() + } + + pub fn deno_exe_path() -> PathBuf { + // Something like /Users/rld/src/deno/target/debug/deps/deno + let mut p = target_dir().join("deno"); + if cfg!(windows) { + p.set_extension("exe"); + } + p + } + + pub struct HttpServerGuard<'a> { + #[allow(dead_code)] + g: MutexGuard<'a, ()>, + child: Child, + } + + impl<'a> Drop for HttpServerGuard<'a> { + fn drop(&mut self) { + match self.child.try_wait() { + Ok(None) => { + self.child.kill().expect("failed to kill http_server.py"); + } + Ok(Some(status)) => { + panic!("http_server.py exited unexpectedly {}", status) + } + Err(e) => panic!("http_server.py err {}", e), + } + } + } + + /// Starts tools/http_server.py when the returned guard is dropped, the server + /// will be killed. + pub fn http_server<'a>() -> HttpServerGuard<'a> { + // TODO(bartlomieju) Allow tests to use the http server in parallel. + let g = GUARD.lock().unwrap(); + + println!("tools/http_server.py starting..."); + let mut child = Command::new("python") + .current_dir(root_path()) + .args(&["-u", "tools/http_server.py"]) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to execute child"); + + let stdout = child.stdout.as_mut().unwrap(); + use std::io::{BufRead, BufReader}; + let lines = BufReader::new(stdout).lines(); + // Wait for "ready" on stdout. See tools/http_server.py + for maybe_line in lines { + if let Ok(line) = maybe_line { + if line.starts_with("ready") { + break; + } + } else { + panic!(maybe_line.unwrap_err()); + } + } + + HttpServerGuard { child, g } + } + + /// Helper function to strip ansi codes. + pub fn strip_ansi_codes(s: &str) -> std::borrow::Cow { + STRIP_ANSI_RE.replace_all(s, "") } pub fn run_and_collect_output( @@ -2505,7 +2591,7 @@ mod util { let mut deno_process_builder = deno_cmd(); deno_process_builder .args(args.split_whitespace()) - .current_dir(&deno::test_util::tests_path()) + .current_dir(&tests_path()) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()); @@ -2543,7 +2629,9 @@ mod util { } pub fn deno_cmd() -> Command { - let mut c = Command::new(deno_exe_path()); + let e = deno_exe_path(); + assert!(e.exists()); + let mut c = Command::new(e); c.env("DENO_DIR", DENO_DIR.path()); c } diff --git a/std/http/file_server.ts b/std/http/file_server.ts index ded930db50..6a2d768056 100755 --- a/std/http/file_server.ts +++ b/std/http/file_server.ts @@ -37,7 +37,7 @@ const serverArgs = parse(args) as FileServerArgs; const CORSEnabled = serverArgs.cors ? true : false; const target = posix.resolve(serverArgs._[1] ?? ""); -const addr = `0.0.0.0:${serverArgs.port ?? serverArgs.p ?? 4500}`; +const addr = `0.0.0.0:${serverArgs.port ?? serverArgs.p ?? 4507}`; const MEDIA_TYPES: Record = { ".md": "text/markdown", diff --git a/std/http/file_server_test.ts b/std/http/file_server_test.ts index 7a36993377..07c8618c11 100644 --- a/std/http/file_server_test.ts +++ b/std/http/file_server_test.ts @@ -36,7 +36,7 @@ function killFileServer(): void { test("file_server serveFile", async (): Promise => { await startFileServer(); try { - const res = await fetch("http://localhost:4500/README.md"); + const res = await fetch("http://localhost:4507/README.md"); assert(res.headers.has("access-control-allow-origin")); assert(res.headers.has("access-control-allow-headers")); assertEquals(res.headers.get("content-type"), "text/markdown"); @@ -53,7 +53,7 @@ test("file_server serveFile", async (): Promise => { test("serveDirectory", async function (): Promise { await startFileServer(); try { - const res = await fetch("http://localhost:4500/"); + const res = await fetch("http://localhost:4507/"); assert(res.headers.has("access-control-allow-origin")); assert(res.headers.has("access-control-allow-headers")); const page = await res.text(); @@ -75,7 +75,7 @@ test("serveDirectory", async function (): Promise { test("serveFallback", async function (): Promise { await startFileServer(); try { - const res = await fetch("http://localhost:4500/badfile.txt"); + const res = await fetch("http://localhost:4507/badfile.txt"); assert(res.headers.has("access-control-allow-origin")); assert(res.headers.has("access-control-allow-headers")); assertEquals(res.status, 404); @@ -88,12 +88,12 @@ test("serveFallback", async function (): Promise { test("serveWithUnorthodoxFilename", async function (): Promise { await startFileServer(); try { - let res = await fetch("http://localhost:4500/http/testdata/%"); + let res = await fetch("http://localhost:4507/http/testdata/%"); assert(res.headers.has("access-control-allow-origin")); assert(res.headers.has("access-control-allow-headers")); assertEquals(res.status, 200); let _ = await res.text(); - res = await fetch("http://localhost:4500/http/testdata/test%20file.txt"); + res = await fetch("http://localhost:4507/http/testdata/test%20file.txt"); assert(res.headers.has("access-control-allow-origin")); assert(res.headers.has("access-control-allow-headers")); assertEquals(res.status, 200);