From 758596c17dcfa3ad28ee40c30ed3ee8a05721786 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 13 May 2020 01:28:12 +0900 Subject: [PATCH] feat: bundle installed script --- cli/installer.rs | 248 +++++++++++++++++++++++------------------------ cli/main.rs | 10 +- 2 files changed, 124 insertions(+), 134 deletions(-) diff --git a/cli/installer.rs b/cli/installer.rs index 6d498a16c68905..53ee9bff0003e5 100644 --- a/cli/installer.rs +++ b/cli/installer.rs @@ -1,5 +1,9 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::flags::Flags; +use crate::fs as deno_fs; +use crate::global_state::GlobalState; +use deno_core::ErrBox; +use deno_core::ModuleSpecifier; use log::Level; use regex::{Regex, RegexBuilder}; use std::env; @@ -79,14 +83,18 @@ deno {} "$@" Ok(()) } -fn generate_config_file( - file_path: PathBuf, - config_file_name: String, -) -> Result<(), Error> { - let config_file_copy_path = get_config_file_path(&file_path); - let cwd = std::env::current_dir().unwrap(); - let config_file_path = cwd.join(config_file_name); - fs::copy(config_file_path, config_file_copy_path)?; +async fn generate_bundle( + flags: Flags, + module_specifier: ModuleSpecifier, + script_path: PathBuf, +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; + let source = global_state + .ts_compiler + .bundle(&global_state, module_specifier) + .await?; + let mut file = File::create(&script_path)?; + file.write_all(source.as_bytes())?; Ok(()) } @@ -128,14 +136,14 @@ fn infer_name_from_url(url: &Url) -> Option { Some(stem) } -pub fn install( +pub async fn install( flags: Flags, module_url: &str, args: Vec, name: Option, root: Option, force: bool, -) -> Result<(), Error> { +) -> Result<(), ErrBox> { let root = if let Some(root) = root { root.canonicalize()? } else { @@ -146,56 +154,54 @@ pub fn install( // ensure directory exists if let Ok(metadata) = fs::metadata(&installation_dir) { if !metadata.is_dir() { - return Err(Error::new( + return Err(ErrBox::from(Error::new( ErrorKind::Other, "Installation path is not a directory", - )); + ))); } } else { fs::create_dir_all(&installation_dir)?; }; - // Check if module_url is remote - let module_url = if is_remote_url(module_url) { - Url::parse(module_url).expect("Should be valid url") - } else { - let module_path = PathBuf::from(module_url); - let module_path = if module_path.is_absolute() { - module_path - } else { - let cwd = env::current_dir().unwrap(); - cwd.join(module_path) - }; - Url::from_file_path(module_path).expect("Path should be absolute") - }; + let mut module_specifier = ModuleSpecifier::resolve_url_or_path(module_url)?; + let module_specifier_ = module_specifier.clone(); + let url = module_specifier_.as_url(); + + // TODO(kt3k): fix this hack in ModuleSpecifier + if url.scheme() == "file" { + let a = deno_fs::normalize_path(&url.to_file_path().unwrap()); + let u = Url::from_file_path(a).unwrap(); + module_specifier = ModuleSpecifier::from(u) + } - let name = name.or_else(|| infer_name_from_url(&module_url)); + let name = name.or_else(|| infer_name_from_url(&url)); let name = match name { Some(name) => name, - None => return Err(Error::new( + None => return Err(ErrBox::from(Error::new( ErrorKind::Other, "An executable name was not provided. One could not be inferred from the URL. Aborting.", - )), + ))), }; validate_name(name.as_str())?; let mut file_path = installation_dir.join(&name); + let script_path = file_path.with_extension("js"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); } if file_path.exists() && !force { - return Err(Error::new( + return Err(ErrBox::from(Error::new( ErrorKind::Other, "Existing installation found. Aborting (Use -f to overwrite).", - )); + ))); }; let mut executable_args = vec!["run".to_string()]; executable_args.extend_from_slice(&flags.to_permission_args()); - if let Some(ca_file) = flags.ca_file { + if let Some(ca_file) = flags.ca_file.clone() { executable_args.push("--cert".to_string()); executable_args.push(ca_file) } @@ -208,10 +214,10 @@ pub fn install( Level::Debug => "debug", Level::Info => "info", _ => { - return Err(Error::new( + return Err(ErrBox::from(Error::new( ErrorKind::Other, format!("invalid log level {}", log_level), - )) + ))) } }; executable_args.push(log_level.to_string()); @@ -226,22 +232,11 @@ pub fn install( executable_args.push("--unstable".to_string()); } - if flags.config_path.is_some() { - let config_file_path = get_config_file_path(&file_path); - let config_file_path_option = config_file_path.to_str(); - if let Some(config_file_path_string) = config_file_path_option { - executable_args.push("--config".to_string()); - executable_args.push(config_file_path_string.to_string()); - } - } - - executable_args.push(module_url.to_string()); + executable_args.push(script_path.to_string_lossy().to_string()); executable_args.extend_from_slice(&args); generate_executable_file(file_path.to_owned(), executable_args)?; - if let Some(config_path) = flags.config_path { - generate_config_file(file_path.to_owned(), config_path)?; - } + generate_bundle(flags, module_specifier, script_path).await?; println!("✅ Successfully installed {}", name); println!("{}", file_path.to_string_lossy()); @@ -270,12 +265,6 @@ fn is_in_path(dir: &PathBuf) -> bool { false } -fn get_config_file_path(file_path: &PathBuf) -> PathBuf { - let mut config_file_copy_path = PathBuf::from(file_path); - config_file_copy_path.set_extension("tsconfig.json"); - config_file_copy_path -} - #[cfg(test)] mod tests { use super::*; @@ -351,9 +340,10 @@ mod tests { assert_eq!(infer_name_from_url(&Url::parse("file:///").unwrap()), None); } - #[test] - fn install_basic() { + #[tokio::test] + async fn install_basic() { let _guard = ENV_LOCK.lock().ok(); + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir_str = temp_dir.path().to_string_lossy().to_string(); // NOTE: this test overrides environmental variables @@ -375,6 +365,7 @@ mod tests { None, false, ) + .await .expect("Install failed"); let mut file_path = temp_dir.path().join(".deno/bin/echo_test"); @@ -388,8 +379,8 @@ mod tests { // It's annoying when shell scripts don't have NL at the end. assert_eq!(content.chars().last().unwrap(), '\n'); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + assert!(content.contains(r#""run""#)); + assert!(content.contains(r#"echo_test.js"#)); if let Some(home) = original_home { env::set_var("HOME", home); } @@ -401,8 +392,9 @@ mod tests { } } - #[test] - fn install_unstable() { + #[tokio::test] + async fn install_unstable() { + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -418,6 +410,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -426,16 +419,17 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); let content = fs::read_to_string(file_path).unwrap(); println!("this is the file path {:?}", content); - assert!(content.contains( - r#""run" "--unstable" "http://localhost:4545/cli/tests/echo_server.ts"# - )); + assert!(content.contains(r#""run" "--unstable""#)); + assert!(content.contains(r#"echo_test.js"#)); } - #[test] - fn install_inferred_name() { + #[tokio::test] + async fn install_inferred_name() { + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -448,6 +442,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_server"); @@ -456,13 +451,16 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); + let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + assert!(content.contains(r#""run""#)); + assert!(content.contains(r#"echo_server.js"#)); } - #[test] - fn install_inferred_name_from_parent() { + #[tokio::test] + async fn install_inferred_name_from_parent() { + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -475,6 +473,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("subdir"); @@ -483,13 +482,17 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); + let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/subdir/main.ts""#)); + assert!(content.contains(r#""run""#)); + assert!(content.contains(r#"subdir.js"#)); } - #[test] - fn install_custom_dir_option() { + #[tokio::test] + async fn install_custom_dir_option() { + let _http_server_guard = test_util::http_server(); + let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -502,6 +505,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -510,14 +514,17 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); + let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + assert!(content.contains(r#""run""#)); + assert!(content.contains(r#"echo_test.js"#)); } - #[test] - fn install_custom_dir_env_var() { + #[tokio::test] + async fn install_custom_dir_env_var() { let _guard = ENV_LOCK.lock().ok(); + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -532,6 +539,7 @@ mod tests { None, false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -540,16 +548,20 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); + let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + assert!(content.contains(r#""run""#)); + assert!(content.contains(r#"echo_test.js"#)); + if let Some(install_root) = original_install_root { env::set_var("DENO_INSTALL_ROOT", install_root); } } - #[test] - fn install_with_flags() { + #[tokio::test] + async fn install_with_flags() { + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -568,6 +580,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -576,17 +589,21 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); + let content = fs::read_to_string(file_path).unwrap(); - assert!(content.contains(r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check" "http://localhost:4545/cli/tests/echo_server.ts" "--foobar""#)); + assert!(content.contains( + r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check""# + )); + assert!(content.contains(r#"echo_test.js" "--foobar""#)); } - #[test] - fn install_local_module() { + #[tokio::test] + async fn install_local_module() { 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 = env::current_dir().unwrap().join("echo_server.ts"); - let local_module_url = Url::from_file_path(&local_module).unwrap(); + let local_module = env::current_dir().unwrap().join("tests/echo_server.ts"); let local_module_str = local_module.to_string_lossy(); install( @@ -597,6 +614,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -605,12 +623,14 @@ mod tests { } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); let content = fs::read_to_string(file_path).unwrap(); - assert!(content.contains(&local_module_url.to_string())); + assert!(content.contains("echo_test.js")); } - #[test] - fn install_force() { + #[tokio::test] + async fn install_force() { + let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new().expect("tempdir fail"); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); @@ -623,6 +643,7 @@ mod tests { Some(temp_dir.path().to_path_buf()), false, ) + .await .expect("Install failed"); let mut file_path = bin_dir.join("echo_test"); @@ -630,16 +651,21 @@ mod tests { file_path = file_path.with_extension("cmd"); } assert!(file_path.exists()); + assert!(file_path.with_extension("js").exists()); // No force. Install failed. let no_force_result = install( - Flags::default(), + Flags { + unstable: true, + ..Flags::default() + }, "http://localhost:4545/cli/tests/cat.ts", // using a different URL vec![], Some("echo_test".to_string()), Some(temp_dir.path().to_path_buf()), false, - ); + ) + .await; assert!(no_force_result.is_err()); assert!(no_force_result .unwrap_err() @@ -647,52 +673,24 @@ mod tests { .contains("Existing installation found")); // Assert not modified let file_content = fs::read_to_string(&file_path).unwrap(); - assert!(file_content.contains("echo_server.ts")); + assert!(!file_content.contains("--unstable")); // Force. Install success. let force_result = install( - Flags::default(), + Flags { + unstable: true, + ..Flags::default() + }, "http://localhost:4545/cli/tests/cat.ts", // using a different URL vec![], Some("echo_test".to_string()), Some(temp_dir.path().to_path_buf()), true, - ); + ) + .await; assert!(force_result.is_ok()); // Assert modified let file_content_2 = fs::read_to_string(&file_path).unwrap(); - assert!(file_content_2.contains("cat.ts")); - } - - #[test] - fn install_with_config() { - let temp_dir = TempDir::new().expect("tempdir fail"); - let bin_dir = temp_dir.path().join("bin"); - let config_file_path = temp_dir.path().join("test_tsconfig.json"); - let config = "{}"; - let mut config_file = File::create(&config_file_path).unwrap(); - let result = config_file.write_all(config.as_bytes()); - assert!(result.is_ok()); - - let result = install( - Flags { - config_path: Some(config_file_path.to_string_lossy().to_string()), - ..Flags::default() - }, - "http://localhost:4545/cli/tests/cat.ts", - vec![], - Some("echo_test".to_string()), - Some(temp_dir.path().to_path_buf()), - true, - ); - eprintln!("result {:?}", result); - assert!(result.is_ok()); - - let config_file_name = "echo_test.tsconfig.json"; - - let file_path = bin_dir.join(config_file_name.to_string()); - assert!(file_path.exists()); - let content = fs::read_to_string(file_path).unwrap(); - assert!(content == "{}"); + assert!(file_content_2.contains("--unstable")); } } diff --git a/cli/main.rs b/cli/main.rs index c1774f5b90659a..65fb33c74914a8 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -334,15 +334,7 @@ async fn install_command( 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 = MainWorker::create(&global_state, main_module.clone())?; - worker.preload_module(&main_module).await?; - installer::install(flags, &module_url, args, name, root, force) - .map_err(ErrBox::from) + installer::install(flags, &module_url, args, name, root, force).await } async fn lint_command(