mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
fix(install): support npm specifiers (#16634)
Supports npm specifiers for `deno install`. This will by default always use a lockfile (which is generated on first run) unless `--no-lock` is specified.
This commit is contained in:
parent
2df0df51a7
commit
d6fd171394
3 changed files with 124 additions and 36 deletions
cli
20
cli/main.rs
20
cli/main.rs
|
@ -205,23 +205,9 @@ async fn install_command(
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
install_flags: InstallFlags,
|
install_flags: InstallFlags,
|
||||||
) -> Result<i32, AnyError> {
|
) -> Result<i32, AnyError> {
|
||||||
let mut preload_flags = flags.clone();
|
let ps = ProcState::build(flags.clone()).await?;
|
||||||
preload_flags.inspect = None;
|
// ensure the module is cached
|
||||||
preload_flags.inspect_brk = None;
|
load_and_type_check(&ps, &[install_flags.module_url.clone()]).await?;
|
||||||
let permissions =
|
|
||||||
Permissions::from_options(&preload_flags.permissions_options())?;
|
|
||||||
let ps = ProcState::build(preload_flags).await?;
|
|
||||||
let main_module = resolve_url_or_path(&install_flags.module_url)?;
|
|
||||||
let mut worker = create_main_worker(
|
|
||||||
&ps,
|
|
||||||
main_module,
|
|
||||||
permissions,
|
|
||||||
vec![],
|
|
||||||
Default::default(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
// First, fetch and compile the module; this step ensures that the module exists.
|
|
||||||
worker.preload_main_module().await?;
|
|
||||||
tools::installer::install(flags, install_flags)?;
|
tools::installer::install(flags, install_flags)?;
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ use crate::args::ConfigFlag;
|
||||||
use crate::args::Flags;
|
use crate::args::Flags;
|
||||||
use crate::args::InstallFlags;
|
use crate::args::InstallFlags;
|
||||||
use crate::args::TypeCheckMode;
|
use crate::args::TypeCheckMode;
|
||||||
use crate::fs_util::canonicalize_path;
|
use crate::fs_util;
|
||||||
|
use crate::npm::NpmPackageReference;
|
||||||
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::resolve_url_or_path;
|
use deno_core::resolve_url_or_path;
|
||||||
|
@ -105,7 +107,9 @@ exec deno {} "$@"
|
||||||
fn get_installer_root() -> Result<PathBuf, io::Error> {
|
fn get_installer_root() -> Result<PathBuf, io::Error> {
|
||||||
if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") {
|
if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") {
|
||||||
if !env_dir.is_empty() {
|
if !env_dir.is_empty() {
|
||||||
return canonicalize_path(&PathBuf::from(env_dir));
|
return fs_util::canonicalize_path_maybe_not_exists(&PathBuf::from(
|
||||||
|
env_dir,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: on Windows, the $HOME environment variable may be set by users or by
|
// Note: on Windows, the $HOME environment variable may be set by users or by
|
||||||
|
@ -125,6 +129,18 @@ fn get_installer_root() -> Result<PathBuf, io::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer_name_from_url(url: &Url) -> Option<String> {
|
pub fn infer_name_from_url(url: &Url) -> Option<String> {
|
||||||
|
if let Ok(npm_ref) = NpmPackageReference::from_specifier(url) {
|
||||||
|
if let Some(sub_path) = npm_ref.sub_path {
|
||||||
|
if !sub_path.contains('/') {
|
||||||
|
return Some(sub_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !npm_ref.req.name.contains('/') {
|
||||||
|
return Some(npm_ref.req.name);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let path = PathBuf::from(url.path());
|
let path = PathBuf::from(url.path());
|
||||||
let mut stem = match path.file_stem() {
|
let mut stem = match path.file_stem() {
|
||||||
Some(stem) => stem.to_string_lossy().to_string(),
|
Some(stem) => stem.to_string_lossy().to_string(),
|
||||||
|
@ -151,7 +167,7 @@ pub fn infer_name_from_url(url: &Url) -> Option<String> {
|
||||||
|
|
||||||
pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
||||||
let root = if let Some(root) = root {
|
let root = if let Some(root) = root {
|
||||||
canonicalize_path(&root)?
|
fs_util::canonicalize_path_maybe_not_exists(&root)?
|
||||||
} else {
|
} else {
|
||||||
get_installer_root()?
|
get_installer_root()?
|
||||||
};
|
};
|
||||||
|
@ -164,7 +180,7 @@ pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file_path = installation_dir.join(&name);
|
let file_path = installation_dir.join(&name);
|
||||||
|
|
||||||
let mut removed = false;
|
let mut removed = false;
|
||||||
|
|
||||||
|
@ -175,7 +191,7 @@ pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
file_path = file_path.with_extension("cmd");
|
let file_path = file_path.with_extension("cmd");
|
||||||
if file_path.exists() {
|
if file_path.exists() {
|
||||||
fs::remove_file(&file_path)?;
|
fs::remove_file(&file_path)?;
|
||||||
println!("deleted {}", file_path.to_string_lossy());
|
println!("deleted {}", file_path.to_string_lossy());
|
||||||
|
@ -189,7 +205,7 @@ pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> {
|
||||||
|
|
||||||
// There might be some extra files to delete
|
// There might be some extra files to delete
|
||||||
for ext in ["tsconfig.json", "lock.json"] {
|
for ext in ["tsconfig.json", "lock.json"] {
|
||||||
file_path = file_path.with_extension(ext);
|
let file_path = file_path.with_extension(ext);
|
||||||
if file_path.exists() {
|
if file_path.exists() {
|
||||||
fs::remove_file(&file_path)?;
|
fs::remove_file(&file_path)?;
|
||||||
println!("deleted {}", file_path.to_string_lossy());
|
println!("deleted {}", file_path.to_string_lossy());
|
||||||
|
@ -259,7 +275,7 @@ fn resolve_shim_data(
|
||||||
install_flags: &InstallFlags,
|
install_flags: &InstallFlags,
|
||||||
) -> Result<ShimData, AnyError> {
|
) -> Result<ShimData, AnyError> {
|
||||||
let root = if let Some(root) = &install_flags.root {
|
let root = if let Some(root) = &install_flags.root {
|
||||||
canonicalize_path(root)?
|
fs_util::canonicalize_path_maybe_not_exists(root)?
|
||||||
} else {
|
} else {
|
||||||
get_installer_root()?
|
get_installer_root()?
|
||||||
};
|
};
|
||||||
|
@ -375,15 +391,31 @@ fn resolve_shim_data(
|
||||||
copy_path.set_extension("tsconfig.json");
|
copy_path.set_extension("tsconfig.json");
|
||||||
executable_args.push("--config".to_string());
|
executable_args.push("--config".to_string());
|
||||||
executable_args.push(copy_path.to_str().unwrap().to_string());
|
executable_args.push(copy_path.to_str().unwrap().to_string());
|
||||||
extra_files.push((copy_path, fs::read_to_string(config_path)?));
|
extra_files.push((
|
||||||
|
copy_path,
|
||||||
|
fs::read_to_string(config_path)
|
||||||
|
.with_context(|| format!("error reading {}", config_path))?,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(lock_path) = &flags.lock {
|
if flags.no_lock {
|
||||||
|
executable_args.push("--no-lock".to_string());
|
||||||
|
} else if flags.lock.is_some()
|
||||||
|
// always use a lockfile for an npm entrypoint unless --no-lock
|
||||||
|
|| NpmPackageReference::from_specifier(&module_url).is_ok()
|
||||||
|
{
|
||||||
let mut copy_path = file_path.clone();
|
let mut copy_path = file_path.clone();
|
||||||
copy_path.set_extension("lock.json");
|
copy_path.set_extension("lock.json");
|
||||||
executable_args.push("--lock".to_string());
|
executable_args.push("--lock".to_string());
|
||||||
executable_args.push(copy_path.to_str().unwrap().to_string());
|
executable_args.push(copy_path.to_str().unwrap().to_string());
|
||||||
extra_files.push((copy_path, fs::read_to_string(lock_path)?));
|
|
||||||
|
if let Some(lock_path) = &flags.lock {
|
||||||
|
extra_files.push((
|
||||||
|
copy_path,
|
||||||
|
fs::read_to_string(lock_path)
|
||||||
|
.with_context(|| format!("error reading {}", lock_path.display()))?,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executable_args.push(module_url.to_string());
|
executable_args.push(module_url.to_string());
|
||||||
|
@ -507,6 +539,22 @@ mod tests {
|
||||||
infer_name_from_url(&Url::parse("file:///@abc/cli.ts").unwrap()),
|
infer_name_from_url(&Url::parse("file:///@abc/cli.ts").unwrap()),
|
||||||
Some("@abc".to_string())
|
Some("@abc".to_string())
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
infer_name_from_url(&Url::parse("npm:cowsay@1.2/cowthink").unwrap()),
|
||||||
|
Some("cowthink".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
infer_name_from_url(&Url::parse("npm:cowsay@1.2/cowthink/test").unwrap()),
|
||||||
|
Some("cowsay".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
infer_name_from_url(&Url::parse("npm:cowsay@1.2").unwrap()),
|
||||||
|
Some("cowsay".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
infer_name_from_url(&Url::parse("npm:@types/node@1.2").unwrap()),
|
||||||
|
None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -692,6 +740,61 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_npm_lockfile_default() {
|
||||||
|
let temp_dir = fs_util::canonicalize_path(&env::temp_dir()).unwrap();
|
||||||
|
let shim_data = resolve_shim_data(
|
||||||
|
&Flags {
|
||||||
|
allow_all: true,
|
||||||
|
..Flags::default()
|
||||||
|
},
|
||||||
|
&InstallFlags {
|
||||||
|
module_url: "npm:cowsay".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
name: None,
|
||||||
|
root: Some(temp_dir.clone()),
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let lock_path = temp_dir
|
||||||
|
.join("bin")
|
||||||
|
.join("cowsay.lock.json")
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
assert_eq!(
|
||||||
|
shim_data.args,
|
||||||
|
vec!["run", "--allow-all", "--lock", &lock_path, "npm:cowsay"]
|
||||||
|
);
|
||||||
|
assert_eq!(shim_data.extra_files, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_npm_no_lock() {
|
||||||
|
let shim_data = resolve_shim_data(
|
||||||
|
&Flags {
|
||||||
|
allow_all: true,
|
||||||
|
no_lock: true,
|
||||||
|
..Flags::default()
|
||||||
|
},
|
||||||
|
&InstallFlags {
|
||||||
|
module_url: "npm:cowsay".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
name: None,
|
||||||
|
root: Some(env::temp_dir()),
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
shim_data.args,
|
||||||
|
vec!["run", "--allow-all", "--no-lock", "npm:cowsay"]
|
||||||
|
);
|
||||||
|
assert_eq!(shim_data.extra_files, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn install_local_module() {
|
fn install_local_module() {
|
||||||
let temp_dir = TempDir::new();
|
let temp_dir = TempDir::new();
|
||||||
|
@ -809,7 +912,6 @@ mod tests {
|
||||||
force: true,
|
force: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
eprintln!("result {:?}", result);
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let config_file_name = "echo_test.tsconfig.json";
|
let config_file_name = "echo_test.tsconfig.json";
|
||||||
|
@ -995,10 +1097,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create extra files
|
// create extra files
|
||||||
file_path = file_path.with_extension("tsconfig.json");
|
{
|
||||||
|
let file_path = file_path.with_extension("tsconfig.json");
|
||||||
File::create(&file_path).unwrap();
|
File::create(&file_path).unwrap();
|
||||||
file_path = file_path.with_extension("lock.json");
|
}
|
||||||
|
{
|
||||||
|
let file_path = file_path.with_extension("lock.json");
|
||||||
File::create(&file_path).unwrap();
|
File::create(&file_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
uninstall("echo_test".to_string(), Some(temp_dir.path().to_path_buf()))
|
uninstall("echo_test".to_string(), Some(temp_dir.path().to_path_buf()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -45,10 +45,6 @@ impl CliMainWorker {
|
||||||
self.worker
|
self.worker
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn preload_main_module(&mut self) -> Result<ModuleId, AnyError> {
|
|
||||||
self.worker.preload_main_module(&self.main_module).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn setup_repl(&mut self) -> Result<(), AnyError> {
|
pub async fn setup_repl(&mut self) -> Result<(), AnyError> {
|
||||||
self.worker.run_event_loop(false).await?;
|
self.worker.run_event_loop(false).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue