Skip to main content
Module

x/deno/cli/tools/installer.rs

A modern runtime for JavaScript and TypeScript.
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::flags::Flags;use crate::flags::InstallFlags;use crate::flags::TypeCheckMode;use crate::fs_util::canonicalize_path;use deno_core::error::generic_error;use deno_core::error::AnyError;use deno_core::resolve_url_or_path;use deno_core::url::Url;use log::Level;use once_cell::sync::Lazy;use regex::Regex;use regex::RegexBuilder;use std::env;use std::fs;use std::fs::File;use std::io;use std::io::Write;use std::path::Path;use std::path::PathBuf;
#[cfg(not(windows))]use std::os::unix::fs::PermissionsExt;
static EXEC_NAME_RE: Lazy<Regex> = Lazy::new(|| { RegexBuilder::new(r"^[a-z][\w-]*$") .case_insensitive(true) .build() .unwrap()});
fn validate_name(exec_name: &str) -> Result<(), AnyError> { if EXEC_NAME_RE.is_match(exec_name) { Ok(()) } else { Err(generic_error(format!( "Invalid executable name: {}", exec_name ))) }}
#[cfg(windows)]/// On Windows, 2 files are generated./// One compatible with cmd & powershell with a .cmd extension/// A second compatible with git bash / MINGW64/// Generate batch script to satisfy that.fn generate_executable_file(shim_data: &ShimData) -> Result<(), AnyError> { let args: Vec<String> = shim_data .args .iter() .map(|c| format!("\"{}\"", c)) .collect(); let template = format!( "% generated by deno install %\n@deno {} %*\n", args .iter() .map(|arg| arg.replace('%', "%%")) .collect::<Vec<_>>() .join(" ") ); let mut file = File::create(&shim_data.file_path)?; file.write_all(template.as_bytes())?;
// write file for bash // create filepath without extensions let template = format!( r#"#!/bin/sh# generated by deno installdeno {} "$@""#, args.join(" "), ); let mut file = File::create(&shim_data.file_path.with_extension(""))?; file.write_all(template.as_bytes())?; Ok(())}
#[cfg(not(windows))]fn generate_executable_file(shim_data: &ShimData) -> Result<(), AnyError> { use shell_escape::escape; let args: Vec<String> = shim_data .args .iter() .map(|c| escape(c.into()).into_owned()) .collect(); let template = format!( r#"#!/bin/sh# generated by deno installexec deno {} "$@""#, args.join(" "), ); let mut file = File::create(&shim_data.file_path)?; file.write_all(template.as_bytes())?; let _metadata = fs::metadata(&shim_data.file_path)?; let mut permissions = _metadata.permissions(); permissions.set_mode(0o755); fs::set_permissions(&shim_data.file_path, permissions)?; Ok(())}
fn get_installer_root() -> Result<PathBuf, io::Error> { if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") { if !env_dir.is_empty() { return canonicalize_path(&PathBuf::from(env_dir)); } } // Note: on Windows, the $HOME environment variable may be set by users or by // third party software, but it is non-standard and should not be relied upon. let home_env_var = if cfg!(windows) { "USERPROFILE" } else { "HOME" }; let mut home_path = env::var_os(home_env_var) .map(PathBuf::from) .ok_or_else(|| { io::Error::new( io::ErrorKind::NotFound, format!("${} is not defined", home_env_var), ) })?; home_path.push(".deno"); Ok(home_path)}
pub fn infer_name_from_url(url: &Url) -> Option<String> { let path = PathBuf::from(url.path()); let mut stem = match path.file_stem() { Some(stem) => stem.to_string_lossy().to_string(), None => return None, }; if stem == "main" || stem == "mod" || stem == "index" || stem == "cli" { if let Some(parent_name) = path.parent().and_then(|p| p.file_name()) { stem = parent_name.to_string_lossy().to_string(); } } let stem = stem.split_once('@').map_or(&*stem, |x| x.0).to_string(); Some(stem)}
pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> { let root = if let Some(root) = root { canonicalize_path(&root)? } else { get_installer_root()? }; let installation_dir = root.join("bin");
// ensure directory exists if let Ok(metadata) = fs::metadata(&installation_dir) { if !metadata.is_dir() { return Err(generic_error("Installation path is not a directory")); } }
let mut file_path = installation_dir.join(&name);
let mut removed = false;
if file_path.exists() { fs::remove_file(&file_path)?; println!("deleted {}", file_path.to_string_lossy()); removed = true };
if cfg!(windows) { file_path = file_path.with_extension("cmd"); if file_path.exists() { fs::remove_file(&file_path)?; println!("deleted {}", file_path.to_string_lossy()); removed = true } }
if !removed { return Err(generic_error(format!("No installation found for {}", name))); }
// There might be some extra files to delete for ext in ["tsconfig.json", "lock.json"] { file_path = file_path.with_extension(ext); if file_path.exists() { fs::remove_file(&file_path)?; println!("deleted {}", file_path.to_string_lossy()); } }
println!("✅ Successfully uninstalled {}", name); Ok(())}
pub fn install( flags: Flags, install_flags: InstallFlags,) -> Result<(), AnyError> { let shim_data = resolve_shim_data(&flags, &install_flags)?;
// ensure directory exists if let Ok(metadata) = fs::metadata(&shim_data.installation_dir) { if !metadata.is_dir() { return Err(generic_error("Installation path is not a directory")); } } else { fs::create_dir_all(&shim_data.installation_dir)?; };
if shim_data.file_path.exists() && !install_flags.force { return Err(generic_error( "Existing installation found. Aborting (Use -f to overwrite).", )); };
generate_executable_file(&shim_data)?; for (path, contents) in shim_data.extra_files { fs::write(path, contents)?; }
println!("✅ Successfully installed {}", shim_data.name); println!("{}", shim_data.file_path.display()); if cfg!(windows) { let display_path = shim_data.file_path.with_extension(""); println!("{} (shell)", display_path.display()); } let installation_dir_str = shim_data.installation_dir.to_string_lossy();
if !is_in_path(&shim_data.installation_dir) { println!("ℹ️ Add {} to PATH", installation_dir_str); if cfg!(windows) { println!(" set PATH=%PATH%;{}", installation_dir_str); } else { println!(" export PATH=\"{}:$PATH\"", installation_dir_str); } }
Ok(())}
struct ShimData { name: String, installation_dir: PathBuf, file_path: PathBuf, args: Vec<String>, extra_files: Vec<(PathBuf, String)>,}
fn resolve_shim_data( flags: &Flags, install_flags: &InstallFlags,) -> Result<ShimData, AnyError> { let root = if let Some(root) = &install_flags.root { canonicalize_path(root)? } else { get_installer_root()? }; let installation_dir = root.join("bin");
// Check if module_url is remote let module_url = resolve_url_or_path(&install_flags.module_url)?;
let name = install_flags .name .clone() .or_else(|| infer_name_from_url(&module_url));
let name = match name { Some(name) => name, None => return Err(generic_error( "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);
if cfg!(windows) { file_path = file_path.with_extension("cmd"); }
let mut extra_files: Vec<(PathBuf, String)> = vec![];
let mut executable_args = vec!["run".to_string()]; executable_args.extend_from_slice(&flags.to_permission_args()); if let Some(url) = flags.location.as_ref() { executable_args.push("--location".to_string()); executable_args.push(url.to_string()); } if let Some(ca_file) = &flags.ca_file { executable_args.push("--cert".to_string()); executable_args.push(ca_file.to_owned()) } if let Some(log_level) = flags.log_level { if log_level == Level::Error { executable_args.push("--quiet".to_string()); } else { executable_args.push("--log-level".to_string()); let log_level = match log_level { Level::Debug => "debug", Level::Info => "info", _ => { return Err(generic_error(format!("invalid log level {}", log_level))) } }; executable_args.push(log_level.to_string()); } }
// we should avoid a default branch here to ensure we continue to cover any // changes to this flag. match flags.type_check_mode { TypeCheckMode::All => (), TypeCheckMode::None => executable_args.push("--no-check".to_string()), TypeCheckMode::Local => { executable_args.push("--no-check=remote".to_string()) } }
if flags.unstable { executable_args.push("--unstable".to_string()); }
if flags.no_remote { executable_args.push("--no-remote".to_string()); }
if flags.lock_write { executable_args.push("--lock-write".to_string()); }
if flags.cached_only { executable_args.push("--cached-only".to_string()); }
if flags.no_prompt { executable_args.push("--no-prompt".to_string()); }
if flags.compat { executable_args.push("--compat".to_string()); }
if !flags.v8_flags.is_empty() { executable_args.push(format!("--v8-flags={}", flags.v8_flags.join(","))); }
if let Some(seed) = flags.seed { executable_args.push("--seed".to_string()); executable_args.push(seed.to_string()); }
if let Some(inspect) = flags.inspect { executable_args.push(format!("--inspect={}", inspect)); }
if let Some(inspect_brk) = flags.inspect_brk { executable_args.push(format!("--inspect-brk={}", inspect_brk)); }
if let Some(import_map_path) = &flags.import_map_path { let import_map_url = resolve_url_or_path(import_map_path)?; executable_args.push("--import-map".to_string()); executable_args.push(import_map_url.to_string()); }
if let Some(config_path) = &flags.config_path { let mut copy_path = file_path.clone(); copy_path.set_extension("tsconfig.json"); executable_args.push("--config".to_string()); executable_args.push(copy_path.to_str().unwrap().to_string()); extra_files.push((copy_path, fs::read_to_string(config_path)?)); }
if let Some(lock_path) = &flags.lock { let mut copy_path = file_path.clone(); copy_path.set_extension("lock.json"); executable_args.push("--lock".to_string()); executable_args.push(copy_path.to_str().unwrap().to_string()); extra_files.push((copy_path, fs::read_to_string(lock_path)?)); }
executable_args.push(module_url.to_string()); executable_args.extend_from_slice(&install_flags.args);
Ok(ShimData { name, installation_dir, file_path, args: executable_args, extra_files, })}
fn is_in_path(dir: &Path) -> bool { if let Some(paths) = env::var_os("PATH") { for p in env::split_paths(&paths) { if *dir == p { return true; } } } false}
#[cfg(test)]mod tests { use super::*;
use std::process::Command; use test_util::testdata_path; use test_util::TempDir;
#[test] fn install_infer_name_from_url() { assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc/server.ts").unwrap() ), Some("server".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc/main.ts").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc/mod.ts").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc/index.ts").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc/cli.ts").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url(&Url::parse("https://example.com/main.ts").unwrap()), Some("main".to_string()) ); assert_eq!( infer_name_from_url(&Url::parse("https://example.com").unwrap()), None ); assert_eq!( infer_name_from_url(&Url::parse("file:///abc/server.ts").unwrap()), Some("server".to_string()) ); assert_eq!( infer_name_from_url(&Url::parse("file:///abc/main.ts").unwrap()), Some("abc".to_string()) ); assert_eq!( infer_name_from_url(&Url::parse("file:///main.ts").unwrap()), Some("main".to_string()) ); assert_eq!(infer_name_from_url(&Url::parse("file:///").unwrap()), None); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc@0.1.0").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc@0.1.0/main.ts").unwrap() ), Some("abc".to_string()) ); assert_eq!( infer_name_from_url( &Url::parse("https://example.com/abc@def@ghi").unwrap() ), Some("abc".to_string()) ); }
#[test] fn install_unstable() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap();
install( Flags { unstable: true, ..Flags::default() }, InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ) .unwrap();
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); }
assert!(file_path.exists());
let content = fs::read_to_string(file_path).unwrap(); println!("this is the file path {:?}", content); if cfg!(windows) { assert!(content.contains( r#""run" "--unstable" "http://localhost:4545/echo_server.ts""# )); } else { assert!(content .contains(r#"run --unstable 'http://localhost:4545/echo_server.ts'"#)); } }
#[test] fn install_inferred_name() { let shim_data = resolve_shim_data( &Flags::default(), &InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: None, root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!(shim_data.name, "echo_server"); assert_eq!( shim_data.args, vec!["run", "http://localhost:4545/echo_server.ts",] ); }
#[test] fn install_inferred_name_from_parent() { let shim_data = resolve_shim_data( &Flags::default(), &InstallFlags { module_url: "http://localhost:4545/subdir/main.ts".to_string(), args: vec![], name: None, root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!(shim_data.name, "subdir"); assert_eq!( shim_data.args, vec!["run", "http://localhost:4545/subdir/main.ts",] ); }
#[test] fn install_custom_dir_option() { let shim_data = resolve_shim_data( &Flags::default(), &InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!(shim_data.name, "echo_test"); assert_eq!( shim_data.args, vec!["run", "http://localhost:4545/echo_server.ts",] ); }
#[test] fn install_with_flags() { let shim_data = resolve_shim_data( &Flags { allow_net: Some(vec![]), allow_read: Some(vec![]), type_check_mode: TypeCheckMode::None, log_level: Some(Level::Error), compat: true, ..Flags::default() }, &InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec!["--foobar".to_string()], name: Some("echo_test".to_string()), root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!(shim_data.name, "echo_test"); assert_eq!( shim_data.args, vec![ "run", "--allow-read", "--allow-net", "--quiet", "--no-check", "--compat", "http://localhost:4545/echo_server.ts", "--foobar", ] ); }
#[test] fn install_prompt() { let shim_data = resolve_shim_data( &Flags { no_prompt: true, ..Flags::default() }, &InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!( shim_data.args, vec!["run", "--no-prompt", "http://localhost:4545/echo_server.ts",] ); }
#[test] fn install_allow_all() { let shim_data = resolve_shim_data( &Flags { allow_all: true, ..Flags::default() }, &InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(env::temp_dir()), force: false, }, ) .unwrap();
assert_eq!( shim_data.args, vec!["run", "--allow-all", "http://localhost:4545/echo_server.ts",] ); }
#[test] fn install_local_module() { let temp_dir = TempDir::new(); 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_str = local_module.to_string_lossy();
install( Flags::default(), InstallFlags { module_url: local_module_str.to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ) .unwrap();
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); }
assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); assert!(content.contains(&local_module_url.to_string())); }
#[test] fn install_force() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap();
install( Flags::default(), InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ) .unwrap();
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); } assert!(file_path.exists());
// No force. Install failed. let no_force_result = install( Flags::default(), InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), // using a different URL args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ); assert!(no_force_result.is_err()); assert!(no_force_result .unwrap_err() .to_string() .contains("Existing installation found")); // Assert not modified let file_content = fs::read_to_string(&file_path).unwrap(); assert!(file_content.contains("echo_server.ts"));
// Force. Install success. let force_result = install( Flags::default(), InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), // using a different URL args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: true, }, ); 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(); 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() }, InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: 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); assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); assert!(content == "{}"); }
// TODO: enable on Windows after fixing batch escaping #[cfg(not(windows))] #[test] fn install_shell_escaping() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap();
install( Flags::default(), InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), args: vec!["\"".to_string()], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ) .unwrap();
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); }
assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); println!("{}", content); if cfg!(windows) { // TODO: see comment above this test } else { assert!( content.contains(r#"run 'http://localhost:4545/echo_server.ts' '"'"#) ); } }
#[test] fn install_unicode() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); let unicode_dir = temp_dir.path().join("Magnús"); std::fs::create_dir(&unicode_dir).unwrap(); let local_module = unicode_dir.join("echo_server.ts"); let local_module_str = local_module.to_string_lossy(); std::fs::write(&local_module, "// Some JavaScript I guess").unwrap();
install( Flags::default(), InstallFlags { module_url: local_module_str.to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: false, }, ) .unwrap();
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); }
// We need to actually run it to make sure the URL is interpreted correctly let status = Command::new(file_path) .env_clear() // use the deno binary in the target directory .env("PATH", test_util::target_dir()) .spawn() .unwrap() .wait() .unwrap(); assert!(status.success()); }
#[test] fn install_with_import_map() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); let import_map_path = temp_dir.path().join("import_map.json"); let import_map_url = Url::from_file_path(&import_map_path).unwrap(); let import_map = "{ \"imports\": {} }"; let mut import_map_file = File::create(&import_map_path).unwrap(); let result = import_map_file.write_all(import_map.as_bytes()); assert!(result.is_ok());
let result = install( Flags { import_map_path: Some(import_map_path.to_string_lossy().to_string()), ..Flags::default() }, InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: true, }, ); assert!(result.is_ok());
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); } assert!(file_path.exists());
let mut expected_string = format!( "--import-map '{}' 'http://localhost:4545/cat.ts'", import_map_url ); if cfg!(windows) { expected_string = format!( "\"--import-map\" \"{}\" \"http://localhost:4545/cat.ts\"", import_map_url ); }
let content = fs::read_to_string(file_path).unwrap(); assert!(content.contains(&expected_string)); }
// Regression test for https://github.com/denoland/deno/issues/10556. #[test] fn install_file_url() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); let module_path = fs::canonicalize(testdata_path().join("cat.ts")).unwrap(); let file_module_string = Url::from_file_path(module_path).unwrap().to_string(); assert!(file_module_string.starts_with("file:///"));
let result = install( Flags::default(), InstallFlags { module_url: file_module_string.to_string(), args: vec![], name: Some("echo_test".to_string()), root: Some(temp_dir.path().to_path_buf()), force: true, }, ); assert!(result.is_ok());
let mut file_path = bin_dir.join("echo_test"); if cfg!(windows) { file_path = file_path.with_extension("cmd"); } assert!(file_path.exists());
let mut expected_string = format!("run '{}'", &file_module_string); if cfg!(windows) { expected_string = format!("\"run\" \"{}\"", &file_module_string); }
let content = fs::read_to_string(file_path).unwrap(); assert!(content.contains(&expected_string)); }
#[test] fn uninstall_basic() { let temp_dir = TempDir::new(); let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap();
let mut file_path = bin_dir.join("echo_test"); File::create(&file_path).unwrap(); if cfg!(windows) { file_path = file_path.with_extension("cmd"); File::create(&file_path).unwrap(); }
// create extra files file_path = file_path.with_extension("tsconfig.json"); File::create(&file_path).unwrap(); file_path = file_path.with_extension("lock.json"); File::create(&file_path).unwrap();
uninstall("echo_test".to_string(), Some(temp_dir.path().to_path_buf())) .unwrap();
assert!(!file_path.exists()); assert!(!file_path.with_extension("tsconfig.json").exists()); assert!(!file_path.with_extension("lock.json").exists());
if cfg!(windows) { file_path = file_path.with_extension("cmd"); assert!(!file_path.exists()); } }}