Skip to content

Commit

Permalink
fix: Track the behavior of corepack enable/disable commands.
Browse files Browse the repository at this point in the history
Signed-off-by: The1111mp <[email protected]>
  • Loading branch information
1111mp committed Nov 23, 2023
1 parent c302655 commit c893321
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 84 deletions.
110 changes: 106 additions & 4 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::{env, ffi::OsString, io::ErrorKind, path::PathBuf, process::ExitStatus};

use fs_extra::file::read_to_string;
#[cfg(windows)]
use fs_extra::file::{copy, CopyOptions};
use fs_extra::file::{read_to_string, remove};
use lazy_static::lazy_static;
use serde_json::{from_str, Value};
use serde_json::{from_str, json, Value};
#[cfg(unix)]
use std::os::unix::fs;
use std::{env, ffi::OsString, io::ErrorKind, path::PathBuf, process::ExitStatus};

lazy_static! {
pub static ref NVMD_PATH: PathBuf = get_nvmd_path();
Expand Down Expand Up @@ -116,6 +119,105 @@ fn get_nvmd_path() -> PathBuf {
}
}

pub fn package_can_be_removed(name: &String) -> bool {
let mut packages_path = NVMD_PATH.clone();
packages_path.push("packages.json");

match read_to_string(&packages_path) {
Err(_) => true,
Ok(content) => {
if content.is_empty() {
return true;
}

let json_obj: Value = from_str(&content).unwrap();

if json_obj.is_null() || !json_obj.is_object() {
return true;
}

if json_obj[name].is_null() || !json_obj[name].is_array() {
return true;
}

let versions = json_obj[name].as_array().unwrap();

if versions.is_empty() {
return true;
}

let target = json!(*VERSION);

if versions.len() == 1 && versions.contains(&target) {
return true;
}

false
}
}
}

pub fn link_package(name: &String) {
let mut source = NVMD_PATH.clone();
source.push("bin");
source.push("nvmd");
let mut alias = NVMD_PATH.clone();
alias.push("bin");
alias.push(name);

fs::symlink(source, alias).unwrap_or_else(|_why| {})
}

#[cfg(windows)]
pub fn link_package(name: &String) {
// from
let mut exe_source = NVMD_PATH.clone();
exe_source.push("bin");
exe_source.push("nvmd.exe");
let mut cmd_source = NVMD_PATH.clone();
cmd_source.push("bin");
cmd_source.push("npm.cmd");

// to
let mut exe_alias = NVMD_PATH.clone();
exe_alias.push("bin");
let exe = name.clone() + ".exe";
exe_alias.push(exe);
let mut cmd_alias = NVMD_PATH.clone();
cmd_alias.push("bin");
let cmd = name.clone() + ".cmd";
cmd_alias.push(cmd);

let mut options = CopyOptions::new(); //Initialize default values for CopyOptions
options.skip_exist = true; // Skip existing files if true (default: false).
copy(&exe_source, &exe_alias, &options).unwrap();
copy(&cmd_source, &cmd_alias, &options).unwrap();
}

#[cfg(unix)]
pub fn unlink_package(name: &String) {
let mut alias = NVMD_PATH.clone();
alias.push("bin");
alias.push(name);

remove(alias).unwrap_or_else(|_why| {});
}

#[cfg(windows)]
pub fn unlink_package(name: &String) {
let mut exe_alias = NVMD_PATH.clone();
exe_alias.push("bin");
let exe = name.clone() + ".exe";
exe_alias.push(exe);
let mut cmd_alias = NVMD_PATH.clone();
cmd_alias.push("bin");
let cmd = name.clone() + ".cmd";
cmd_alias.push(cmd);

remove(exe_alias).unwrap_or_else(|_why| {});
remove(cmd_alias).unwrap_or_else(|_why| {});
}

fn default_home_dir() -> Result<PathBuf, ErrorKind> {
let mut home = dirs::home_dir().ok_or(ErrorKind::NotFound)?;
home.push(".nvmd");
Expand Down
122 changes: 122 additions & 0 deletions src/run/corepack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use super::{ExitStatus, OsStr, OsString};

use lazy_static::lazy_static;

use crate::{
command as CommandTool,
common::{link_package, package_can_be_removed, unlink_package, ENV_PATH},
};

lazy_static! {
static ref ENABLE: OsString = OsString::from("enable");
static ref DISABLE: OsString = OsString::from("disable");
static ref INSTALL_DIRECTORY: OsString = OsString::from("--install-directory");
}

// corepack enable --install-directory /path/to/folder

// When run, this commmand will check whether the shims for the specified package
// managers can be found with the correct values inside the install directory. If
// not, or if they don't exist, they will be created.

// By default it will locate the install directory by running the equivalent of
// `which corepack`, but this can be tweaked by explicitly passing the install
// directory via the `--install-directory` flag.

pub(super) fn command(exe: &OsStr, args: &[OsString]) -> Result<ExitStatus, String> {
if ENV_PATH.is_empty() {
return Err(String::from("command not found: ") + exe.to_str().unwrap());
}

let child = CommandTool::create_command(exe)
.env("PATH", ENV_PATH.clone())
.args(args)
.status();

match child {
Ok(status) => {
let install_directory = args.contains(&INSTALL_DIRECTORY);
// corepack enable ..
// No special handling is required when using the "--install-directory" option
if args.contains(&ENABLE) && !install_directory {
// let packages = &args.into_iter().filter(is_positional).collect();
corepack_enable(args);
}

// corepack disable ..
// No special handling is required when using the "--install-directory" option
if args.contains(&DISABLE) && !install_directory {
corepack_disable(args);
}

Ok(status)
}
Err(_) => Err(String::from("failed to execute process")),
}
}

fn corepack_enable(args: &[OsString]) {
let packages = &mut args
.into_iter()
.filter(is_positional)
.map(|name| String::from(name.to_str().unwrap()))
.collect::<Vec<String>>();

if packages.is_empty() {
packages.push(String::from("yarn"));
packages.push(String::from("pnpm"));
}

for package in packages {
link_package(package);
if package == "yarn" {
link_package(&String::from("yarnpkg"));
}
if package == "pnpm" {
link_package(&String::from("pnpx"));
}
}
}

fn corepack_disable(args: &[OsString]) {
let packages = &mut args
.into_iter()
.filter(is_positional)
.map(|name| String::from(name.to_str().unwrap()))
.collect::<Vec<String>>();

if packages.is_empty() {
packages.push(String::from("yarn"));
packages.push(String::from("pnpm"));
}

for package in packages {
if package_can_be_removed(package) {
// need to remove executable file
unlink_package(package);
if package == "yarn" {
unlink_package(&String::from("yarnpkg"));
}
if package == "pnpm" {
unlink_package(&String::from("pnpx"));
}
}
}
}

fn is_flag<A>(arg: &A) -> bool
where
A: AsRef<OsStr>,
{
match arg.as_ref().to_str() {
Some(a) => a == "yarn" || a == "pnpm",
None => false,
}
}

fn is_positional<A>(arg: &A) -> bool
where
A: AsRef<OsStr>,
{
is_flag(arg)
}
4 changes: 3 additions & 1 deletion src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{
};

mod binary;
mod corepack;
mod engine;
mod npm;
mod nvmd;
Expand All @@ -19,7 +20,8 @@ pub fn execute() -> Result<ExitStatus, String> {
match exe.to_str() {
Some("nvmd") => nvmd::command(),
Some("npm") => npm::command(&exe, &args),
Some("node") | Some("npx") | Some("corepack") => engine::command(&exe, &args),
Some("corepack") => corepack::command(&exe, &args),
Some("node") | Some("npx") => engine::command(&exe, &args),
_ => binary::command(&exe, &args),
}
}
Expand Down
84 changes: 5 additions & 79 deletions src/run/npm.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use fs_extra::file::{read_to_string, remove, write_all};
use fs_extra::file::{read_to_string, write_all};
use lazy_static::lazy_static;
use regex::Regex;
use serde_json::{from_str, json, Value};
#[cfg(unix)]
use std::os::unix::fs;
use std::{
io::{BufRead, BufReader},
path::PathBuf,
Expand All @@ -14,7 +12,7 @@ use std::{
use super::{ExitStatus, OsStr, OsString};
use crate::{
command as CommandTool,
common::{ENV_PATH, NVMD_PATH, VERSION},
common::{link_package, unlink_package, ENV_PATH, NVMD_PATH, VERSION},
};

lazy_static! {
Expand Down Expand Up @@ -157,7 +155,7 @@ fn record_uninstall_package(name: &String) -> bool {
let mut packages_path = NVMD_PATH.clone();
packages_path.push("packages.json");

let record = match read_to_string(&packages_path) {
match read_to_string(&packages_path) {
Err(_) => true,
Ok(content) => {
if content.is_empty() {
Expand Down Expand Up @@ -198,9 +196,7 @@ fn record_uninstall_package(name: &String) -> bool {

return ret;
}
};

return record;
}
}

fn collection_packages_name(args: &[OsString]) {
Expand Down Expand Up @@ -285,82 +281,12 @@ fn get_package_bin_names(npm_perfix: &String, packages: &Vec<OsString>) -> Vec<S
package_bin_names
}

#[cfg(unix)]
fn link_package(name: &String) {
let mut source = NVMD_PATH.clone();
source.push("bin");
source.push("nvmd");
let mut alias = NVMD_PATH.clone();
alias.push("bin");
alias.push(name);

fs::symlink(source, alias).unwrap_or_else(|_why| {})
}

#[cfg(windows)]
fn link_package(name: &String) {
use fs_extra::file::{copy, CopyOptions};
// from
let mut exe_source = NVMD_PATH.clone();
exe_source.push("bin");
exe_source.push("nvmd.exe");
let mut cmd_source = NVMD_PATH.clone();
cmd_source.push("bin");
cmd_source.push("npm.cmd");

// to
let mut exe_alias = NVMD_PATH.clone();
exe_alias.push("bin");
let exe = name.clone() + ".exe";
exe_alias.push(exe);
let mut cmd_alias = NVMD_PATH.clone();
cmd_alias.push("bin");
let cmd = name.clone() + ".cmd";
cmd_alias.push(cmd);

let mut options = CopyOptions::new(); //Initialize default values for CopyOptions
options.skip_exist = true; // Skip existing files if true (default: false).
copy(&exe_source, &exe_alias, &options).unwrap();
copy(&cmd_source, &cmd_alias, &options).unwrap();
}

#[cfg(unix)]
fn unlink_package(name: &String) {
let mut alias = NVMD_PATH.clone();
alias.push("bin");
alias.push(name);

remove(alias).unwrap_or_else(|_why| {});
}

#[cfg(windows)]
fn unlink_package(name: &String) {
let mut exe_alias = NVMD_PATH.clone();
exe_alias.push("bin");
let exe = name.clone() + ".exe";
exe_alias.push(exe);
let mut cmd_alias = NVMD_PATH.clone();
cmd_alias.push("bin");
let cmd = name.clone() + ".cmd";
cmd_alias.push(cmd);

remove(exe_alias).unwrap_or_else(|_why| {});
remove(cmd_alias).unwrap_or_else(|_why| {});
}

fn is_flag<A>(arg: &A) -> bool
where
A: AsRef<OsStr>,
{
match arg.as_ref().to_str() {
Some(a) => {
a.starts_with('-')
|| a == "install"
|| a == "i"
|| a == "uninstall"
|| a.starts_with("yarn")
|| a.starts_with("pnpm")
}
Some(a) => a.starts_with('-') || a == "install" || a == "i" || a == "uninstall",
None => false,
}
}
Expand Down

0 comments on commit c893321

Please sign in to comment.