Browse Source

Allow recursive version lookups (#607)

remotes/origin/feat/support-install-latest
Gal Schlezinger 3 years ago committed by GitHub
parent
commit
207ef22f32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 223
      docs/commands.md
  2. 7
      src/commands/env.rs
  3. 2
      src/commands/exec.rs
  4. 2
      src/commands/install.rs
  5. 2
      src/commands/uninstall.rs
  6. 70
      src/commands/use.rs
  7. 22
      src/config.rs
  8. 9
      src/default_version.rs
  9. 2
      src/main.rs
  10. 32
      src/shell/bash.rs
  11. 26
      src/shell/fish.rs
  12. 33
      src/shell/powershell.rs
  13. 12
      src/shell/windows_cmd/cd.cmd
  14. 30
      src/shell/zsh.rs
  15. 15
      src/user_version_reader.rs
  16. 41
      src/version_file_strategy.rs
  17. 34
      src/version_files.rs

223
docs/commands.md

@ -28,6 +28,14 @@ OPTIONS: @@ -28,6 +28,14 @@ OPTIONS:
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
SUBCOMMANDS:
alias Alias a version to a common name
@ -55,24 +63,42 @@ USAGE: @@ -55,24 +63,42 @@ USAGE:
fnm alias [OPTIONS] <to-version> <name>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<to-version>
<name>
```
# `fnm completions`
@ -85,14 +111,20 @@ USAGE: @@ -85,14 +111,20 @@ USAGE:
fnm completions [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
@ -102,6 +134,14 @@ OPTIONS: @@ -102,6 +134,14 @@ OPTIONS:
--shell <shell>
The shell syntax to use. Infers when missing [possible values: zsh, bash, fish, powershell, elvish]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
```
# `fnm current`
@ -114,20 +154,34 @@ USAGE: @@ -114,20 +154,34 @@ USAGE:
fnm current [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
```
# `fnm default`
@ -162,6 +216,14 @@ OPTIONS: @@ -162,6 +216,14 @@ OPTIONS:
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<version>
@ -211,6 +273,14 @@ OPTIONS: @@ -211,6 +273,14 @@ OPTIONS:
--shell <shell>
The shell syntax to use. Infers when missing [possible values: bash, zsh, fish, powershell]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
```
# `fnm exec`
@ -251,6 +321,14 @@ OPTIONS: @@ -251,6 +321,14 @@ OPTIONS:
--using <version>
Either an explicit version, or a filename with the version written in it
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<arguments>...
@ -274,24 +352,42 @@ USAGE: @@ -274,24 +352,42 @@ USAGE:
fnm install [FLAGS] [OPTIONS] [version]
FLAGS:
-h, --help Prints help information
--lts Install latest LTS
-V, --version Prints version information
-h, --help
Prints help information
--lts
Install latest LTS
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<version> A version string. Can be a partial semver or a LTS version name by the format lts/NAME
<version>
A version string. Can be a partial semver or a LTS version name by the format lts/NAME
```
# `fnm list`
@ -304,20 +400,34 @@ USAGE: @@ -304,20 +400,34 @@ USAGE:
fnm list [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
```
# `fnm list-remote`
@ -330,20 +440,34 @@ USAGE: @@ -330,20 +440,34 @@ USAGE:
fnm list-remote [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
```
# `fnm unalias`
@ -356,23 +480,39 @@ USAGE: @@ -356,23 +480,39 @@ USAGE:
fnm unalias [OPTIONS] <requested-alias>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-h, --help
Prints help information
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<requested-alias>
```
# `fnm uninstall`
@ -408,6 +548,14 @@ OPTIONS: @@ -408,6 +548,14 @@ OPTIONS:
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<version>
@ -425,22 +573,43 @@ USAGE: @@ -425,22 +573,43 @@ USAGE:
fnm use [FLAGS] [OPTIONS] [version]
FLAGS:
-h, --help Prints help information
--install-if-missing Install the version if it isn't installed yet
-V, --version Prints version information
-h, --help
Prints help information
--install-if-missing
Install the version if it isn't installed yet
--silent-if-unchanged
Don't output a message identifying the version being used if it will not change due to execution of this
command
-V, --version
Prints version information
OPTIONS:
--arch <arch>
Override the architecture of the installed Node binary. Defaults to arch of fnm binary [env: FNM_ARCH]
[default: x64]
--fnm-dir <base-dir> The root directory of fnm installations [env: FNM_DIR]
--fnm-dir <base-dir>
The root directory of fnm installations [env: FNM_DIR]
--log-level <log-level>
The log level of fnm commands [env: FNM_LOGLEVEL] [default: info] [possible values: quiet, info, all,
error]
--node-dist-mirror <node-dist-mirror>
https://nodejs.org/dist/ mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist]
--version-file-strategy <version-file-strategy>
A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is called without a
version, or when `--use-on-cd` is configured on evaluation.
* `local`: Use the local version of Node defined within the current directory
* `recursive`: Use the version of Node defined within the current directory and all parent directories [env:
FNM_VERSION_FILE_STRATEGY] [default: local] [possible values: local, recursive]
ARGS:
<version>
```

7
src/commands/env.rs

@ -71,6 +71,13 @@ impl Command for Env { @@ -71,6 +71,13 @@ impl Command for Env {
"{}",
shell.set_env_var("FNM_MULTISHELL_PATH", multishell_path.to_str().unwrap())
);
println!(
"{}",
shell.set_env_var(
"FNM_VERSION_FILE_STRATEGY",
config.version_file_strategy().as_str()
)
);
println!(
"{}",
shell.set_env_var("FNM_DIR", config.base_dir_with_default().to_str().unwrap())

2
src/commands/exec.rs

@ -46,7 +46,7 @@ impl Cmd for Exec { @@ -46,7 +46,7 @@ impl Cmd for Exec {
let current_dir = std::env::current_dir().unwrap();
UserVersionReader::Path(current_dir)
})
.into_user_version()
.into_user_version(config)
.context(CantInferVersion)?;
let applicable_version = choose_version_for_user_input(&version, config)

2
src/commands/install.rs

@ -50,7 +50,7 @@ impl super::command::Command for Install { @@ -50,7 +50,7 @@ impl super::command::Command for Install {
let current_version = self
.version()?
.or_else(|| get_user_version_for_directory(current_dir))
.or_else(|| get_user_version_for_directory(current_dir, config))
.context(CantInferVersion)?;
let version = match current_version.clone() {

2
src/commands/uninstall.rs

@ -26,7 +26,7 @@ impl Command for Uninstall { @@ -26,7 +26,7 @@ impl Command for Uninstall {
.version
.or_else(|| {
let current_dir = std::env::current_dir().unwrap();
get_user_version_for_directory(current_dir)
get_user_version_for_directory(current_dir, config)
})
.context(CantInferVersion)?;

70
src/commands/use.rs

@ -1,14 +1,17 @@ @@ -1,14 +1,17 @@
use super::command::Command;
use super::install::Install;
use crate::current_version::current_version;
use crate::fs;
use crate::installed_versions;
use crate::outln;
use crate::system_version;
use crate::user_version::UserVersion;
use crate::version::Version;
use crate::version_file_strategy::VersionFileStrategy;
use crate::{config::FnmConfig, user_version_reader::UserVersionReader};
use colored::Colorize;
use snafu::{ensure, OptionExt, ResultExt, Snafu};
use std::path::Path;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
@ -17,6 +20,11 @@ pub struct Use { @@ -17,6 +20,11 @@ pub struct Use {
/// Install the version if it isn't installed yet
#[structopt(long)]
install_if_missing: bool,
/// Don't output a message identifying the version being used
/// if it will not change due to execution of this command
#[structopt(long)]
silent_if_unchanged: bool,
}
impl Command for Use {
@ -34,32 +42,34 @@ impl Command for Use { @@ -34,32 +42,34 @@ impl Command for Use {
let current_dir = std::env::current_dir().unwrap();
UserVersionReader::Path(current_dir)
})
.into_user_version()
.into_user_version(config)
.ok_or_else(|| match config.version_file_strategy() {
VersionFileStrategy::Local => InferVersionError::Local,
VersionFileStrategy::Recursive => InferVersionError::Recursive,
})
.context(CantInferVersion)?;
let version_path = if let UserVersion::Full(Version::Bypassed) = requested_version {
outln!(
config,
Info,
let (message, version_path) = if let UserVersion::Full(Version::Bypassed) =
requested_version
{
let message = format!(
"Bypassing fnm: using {} node",
system_version::display_name().cyan()
);
system_version::path()
(message, system_version::path())
} else if let Some(alias_name) = requested_version.alias_name() {
let alias_path = config.aliases_dir().join(&alias_name);
let system_path = system_version::path();
if matches!(fs::shallow_read_symlink(&alias_path), Ok(shallow_path) if shallow_path == system_path)
{
outln!(
config,
Info,
let message = format!(
"Bypassing fnm: using {} node",
system_version::display_name().cyan()
);
system_path
(message, system_path)
} else if alias_path.exists() {
outln!(config, Info, "Using Node for alias {}", alias_name.cyan());
alias_path
let message = format!("Using Node for alias {}", alias_name.cyan());
(message, alias_path)
} else {
install_new_version(requested_version, config, self.install_if_missing)?;
return Ok(());
@ -67,23 +77,36 @@ impl Command for Use { @@ -67,23 +77,36 @@ impl Command for Use {
} else {
let current_version = requested_version.to_version(&all_versions, config);
if let Some(version) = current_version {
outln!(config, Info, "Using Node {}", version.to_string().cyan());
config
let version_path = config
.installations_dir()
.join(version.to_string())
.join("installation")
.join("installation");
let message = format!("Using Node {}", version.to_string().cyan());
(message, version_path)
} else {
install_new_version(requested_version, config, self.install_if_missing)?;
return Ok(());
}
};
if !self.silent_if_unchanged || will_version_change(&version_path, config) {
outln!(config, Info, "{}", message);
}
replace_symlink(&version_path, multishell_path).context(SymlinkingCreationIssue)?;
Ok(())
}
}
fn will_version_change(resolved_path: &Path, config: &FnmConfig) -> bool {
let current_version_path = current_version(config)
.unwrap_or(None)
.map(|v| v.installation_path(config));
current_version_path.as_deref() != Some(resolved_path)
}
fn install_new_version(
requested_version: UserVersion,
config: &FnmConfig,
@ -106,6 +129,7 @@ fn install_new_version( @@ -106,6 +129,7 @@ fn install_new_version(
Use {
version: Some(UserVersionReader::Direct(requested_version)),
install_if_missing: true,
silent_if_unchanged: false,
}
.apply(config)?;
@ -187,10 +211,8 @@ pub enum Error { @@ -187,10 +211,8 @@ pub enum Error {
VersionListingError { source: installed_versions::Error },
#[snafu(display("Requested version {} is not currently installed", version))]
CantFindVersion { version: UserVersion },
#[snafu(display(
"Can't find version in dotfiles. Please provide a version manually to the command."
))]
CantInferVersion,
#[snafu(display("{}", source))]
CantInferVersion { source: InferVersionError },
#[snafu(display(
"{}\n{}\n{}",
"We can't find the necessary environment variables to replace the Node version.",
@ -199,3 +221,13 @@ pub enum Error { @@ -199,3 +221,13 @@ pub enum Error {
))]
FnmEnvWasNotSourced,
}
#[derive(Debug, Snafu)]
pub enum InferVersionError {
#[snafu(display(
"Can't find version in dotfiles. Please provide a version manually to the command."
))]
Local,
#[snafu(display("Could not find any version to use. Maybe you don't have a default version set?\nTry running `fnm default <VERSION>` to set one,\nor create a .node-version file inside your project to declare a Node.js version."))]
Recursive,
}

22
src/config.rs

@ -2,6 +2,7 @@ use crate::arch::Arch; @@ -2,6 +2,7 @@ use crate::arch::Arch;
use crate::log_level::LogLevel;
use crate::outln;
use crate::path_ext::PathExt;
use crate::version_file_strategy::VersionFileStrategy;
use colored::Colorize;
use dirs::{data_dir, home_dir};
use std::sync::atomic::{AtomicBool, Ordering};
@ -63,6 +64,22 @@ pub struct FnmConfig { @@ -63,6 +64,22 @@ pub struct FnmConfig {
hide_env_values = true
)]
pub arch: Arch,
/// A strategy for how to resolve the Node version. Used whenever `fnm use` or `fnm install` is
/// called without a version, or when `--use-on-cd` is configured on evaluation.
///
/// * `local`: Use the local version of Node defined within the current directory
///
/// * `recursive`: Use the version of Node defined within the current directory and all parent directories
#[structopt(
long,
env = "FNM_VERSION_FILE_STRATEGY",
possible_values = VersionFileStrategy::possible_values(),
default_value = "local",
global = true,
hide_env_values = true,
)]
version_file_strategy: VersionFileStrategy,
}
impl Default for FnmConfig {
@ -73,11 +90,16 @@ impl Default for FnmConfig { @@ -73,11 +90,16 @@ impl Default for FnmConfig {
multishell_path: None,
log_level: LogLevel::Info,
arch: Arch::default(),
version_file_strategy: VersionFileStrategy::default(),
}
}
}
impl FnmConfig {
pub fn version_file_strategy(&self) -> &VersionFileStrategy {
&self.version_file_strategy
}
pub fn multishell_path(&self) -> Option<&std::path::Path> {
match &self.multishell_path {
None => None,

9
src/default_version.rs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
use crate::config::FnmConfig;
use crate::version::Version;
use std::str::FromStr;
pub fn find_default_version(config: &FnmConfig) -> Option<Version> {
let version_path = config.default_version_dir().canonicalize().ok()?;
let file_name = version_path.parent()?.file_name()?;
Version::from_str(file_name.to_str()?).ok()?.into()
}

2
src/main.rs

@ -28,10 +28,12 @@ mod system_version; @@ -28,10 +28,12 @@ mod system_version;
mod user_version;
mod user_version_reader;
mod version;
mod version_file_strategy;
mod version_files;
#[macro_use]
mod log_level;
mod default_version;
fn main() {
env_logger::init();

32
src/shell/bash.rs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
use crate::version_file_strategy::VersionFileStrategy;
use super::shell::Shell;
use indoc::indoc;
use indoc::{formatdoc, indoc};
use std::path::Path;
#[derive(Debug)]
@ -18,24 +20,32 @@ impl Shell for Bash { @@ -18,24 +20,32 @@ impl Shell for Bash {
format!("export {}={:?}", name, value)
}
fn use_on_cd(&self, _config: &crate::config::FnmConfig) -> String {
indoc!(
r#"
__fnm_use_if_file_found() {
fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String {
let autoload_hook = match config.version_file_strategy() {
VersionFileStrategy::Local => indoc!(
r#"
if [[ -f .node-version || -f .nvmrc ]]; then
fnm use
fnm use --silent-if-unchanged
fi
}
"#
),
VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#,
};
formatdoc!(
r#"
__fnm_use_if_file_found() {{
{autoload_hook}
}}
__fnmcd() {
__fnmcd() {{
\cd "$@" || return $?
__fnm_use_if_file_found
}
}}
alias cd=__fnmcd
__fnm_use_if_file_found
"#
"#,
autoload_hook = autoload_hook
)
.into()
}
}

26
src/shell/fish.rs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
use crate::version_file_strategy::VersionFileStrategy;
use super::shell::Shell;
use indoc::indoc;
use indoc::{formatdoc, indoc};
use std::path::Path;
#[derive(Debug)]
@ -18,19 +20,27 @@ impl Shell for Fish { @@ -18,19 +20,27 @@ impl Shell for Fish {
format!("set -gx {name} {value:?};", name = name, value = value)
}
fn use_on_cd(&self, _config: &crate::config::FnmConfig) -> String {
indoc!(
fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String {
let autoload_hook = match config.version_file_strategy() {
VersionFileStrategy::Local => indoc!(
r#"
if test -f .node-version -o -f .nvmrc
fnm use --silent-if-unchanged
end
"#
),
VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#,
};
formatdoc!(
r#"
function _fnm_autoload_hook --on-variable PWD --description 'Change Node version on directory change'
status --is-command-substitution; and return
if test -f .node-version -o -f .nvmrc
fnm use
end
{autoload_hook}
end
_fnm_autoload_hook
"#
"#,
autoload_hook = autoload_hook
)
.into()
}
}

33
src/shell/powershell.rs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
use crate::version_file_strategy::VersionFileStrategy;
use super::Shell;
use indoc::indoc;
use indoc::{formatdoc, indoc};
use std::path::Path;
#[derive(Debug)]
@ -18,15 +20,26 @@ impl Shell for PowerShell { @@ -18,15 +20,26 @@ impl Shell for PowerShell {
format!(r#"$env:{} = "{}""#, name, value)
}
fn use_on_cd(&self, _config: &crate::config::FnmConfig) -> String {
indoc!(r#"
function Set-FnmOnLoad { If ((Test-Path .nvmrc) -Or (Test-Path .node-version)) { & fnm use } }
function Set-LocationWithFnm { param($path); Set-Location $path; Set-FnmOnLoad }
Set-Alias cd_with_fnm Set-LocationWithFnm -Force
Remove-Item alias:\cd
New-Alias cd Set-LocationWithFnm
Set-FnmOnLoad
"#).into()
fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String {
let autoload_hook = match config.version_file_strategy() {
VersionFileStrategy::Local => indoc!(
r#"
If ((Test-Path .nvmrc) -Or (Test-Path .node-version)) { & fnm use --silent-if-unchanged }
"#
),
VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#,
};
formatdoc!(
r#"
function Set-FnmOnLoad {{ {autoload_hook} }}
function Set-LocationWithFnm {{ param($path); Set-Location $path; Set-FnmOnLoad }}
Set-Alias cd_with_fnm Set-LocationWithFnm -Force
Remove-Item alias:\cd
New-Alias cd Set-LocationWithFnm
Set-FnmOnLoad
"#,
autoload_hook = autoload_hook
)
}
fn to_structopt_shell(&self) -> clap::Shell {
clap::Shell::PowerShell

12
src/shell/windows_cmd/cd.cmd

@ -1,10 +1,14 @@ @@ -1,10 +1,14 @@
@echo off
cd %1
if exist .nvmrc (
fnm use
if "%FNM_VERSION_FILE_STRATEGY%" == "recursive" (
fnm use --silent-if-unchanged
) else (
if exist .nvmrc (
fnm use --silent-if-unchanged
) else (
if exist .node-version (
fnm use
fnm use --silent-if-unchanged
)
)
)
@echo on
@echo on

30
src/shell/zsh.rs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
use crate::version_file_strategy::VersionFileStrategy;
use super::shell::Shell;
use indoc::indoc;
use indoc::{formatdoc, indoc};
use std::path::Path;
#[derive(Debug)]
@ -22,20 +24,28 @@ impl Shell for Zsh { @@ -22,20 +24,28 @@ impl Shell for Zsh {
Some("rehash".to_string())
}
fn use_on_cd(&self, _config: &crate::config::FnmConfig) -> String {
indoc!(
r#"
autoload -U add-zsh-hook
_fnm_autoload_hook () {
fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String {
let autoload_hook = match config.version_file_strategy() {
VersionFileStrategy::Local => indoc!(
r#"
if [[ -f .node-version || -f .nvmrc ]]; then
fnm use
fnm use --silent-if-unchanged
fi
}
"#
),
VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#,
};
formatdoc!(
r#"
autoload -U add-zsh-hook
_fnm_autoload_hook () {{
{autoload_hook}
}}
add-zsh-hook chpwd _fnm_autoload_hook \
&& _fnm_autoload_hook
"#
"#,
autoload_hook = autoload_hook
)
.into()
}
}

15
src/user_version_reader.rs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
use crate::config::FnmConfig;
use crate::user_version::UserVersion;
use crate::version_files::{get_user_version_for_directory, get_user_version_for_file};
use std::path::PathBuf;
@ -10,11 +11,11 @@ pub enum UserVersionReader { @@ -10,11 +11,11 @@ pub enum UserVersionReader {
}
impl UserVersionReader {
pub fn into_user_version(self) -> Option<UserVersion> {
pub fn into_user_version(self, config: &FnmConfig) -> Option<UserVersion> {
match self {
Self::Direct(uv) => Some(uv),
Self::Path(pathbuf) if pathbuf.is_file() => get_user_version_for_file(&pathbuf),
Self::Path(pathbuf) => get_user_version_for_directory(&pathbuf),
Self::Path(pathbuf) => get_user_version_for_directory(&pathbuf, config),
}
}
}
@ -47,7 +48,8 @@ mod tests { @@ -47,7 +48,8 @@ mod tests {
write!(file, "14").unwrap();
let pathbuf = file.path().to_path_buf();
let user_version = UserVersionReader::Path(pathbuf).into_user_version();
let user_version =
UserVersionReader::Path(pathbuf).into_user_version(&FnmConfig::default());
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
}
@ -58,14 +60,15 @@ mod tests { @@ -58,14 +60,15 @@ mod tests {
std::fs::write(node_version_path, "14").unwrap();
let pathbuf = directory.path().to_path_buf();
let user_version = UserVersionReader::Path(pathbuf).into_user_version();
let user_version =
UserVersionReader::Path(pathbuf).into_user_version(&FnmConfig::default());
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
}
#[test]
fn test_direct_to_version() {
let user_version =
UserVersionReader::Direct(UserVersion::OnlyMajor(14)).into_user_version();
let user_version = UserVersionReader::Direct(UserVersion::OnlyMajor(14))
.into_user_version(&FnmConfig::default());
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
}

41
src/version_file_strategy.rs

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
use std::str::FromStr;
#[derive(Debug)]
pub enum VersionFileStrategy {
Local,
Recursive,
}
impl VersionFileStrategy {
pub fn possible_values() -> &'static [&'static str] {
&["local", "recursive"]
}
pub fn as_str(&self) -> &'static str {
match self {
VersionFileStrategy::Local => "local",
VersionFileStrategy::Recursive => "recursive",
}
}
}
impl Default for VersionFileStrategy {
fn default() -> Self {
VersionFileStrategy::Local
}
}
impl FromStr for VersionFileStrategy {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"local" => Ok(VersionFileStrategy::Local),
"recursive" => Ok(VersionFileStrategy::Recursive),
_ => Err(format!(
"Invalid strategy: {}. Expected one of: local, recursive",
s
)),
}
}
}

34
src/version_files.rs

@ -1,4 +1,7 @@ @@ -1,4 +1,7 @@
use crate::config::FnmConfig;
use crate::default_version;
use crate::user_version::UserVersion;
use crate::version_file_strategy::VersionFileStrategy;
use encoding_rs_io::DecodeReaderBytes;
use log::info;
use std::io::Read;
@ -7,7 +10,36 @@ use std::str::FromStr; @@ -7,7 +10,36 @@ use std::str::FromStr;
const PATH_PARTS: [&str; 2] = [".nvmrc", ".node-version"];
pub fn get_user_version_for_directory(path: impl AsRef<Path>) -> Option<UserVersion> {
pub fn get_user_version_for_directory(
path: impl AsRef<Path>,
config: &FnmConfig,
) -> Option<UserVersion> {
match config.version_file_strategy() {
VersionFileStrategy::Local => get_user_version_for_single_directory(path),
VersionFileStrategy::Recursive => {
get_user_version_for_directory_recursive(path).or_else(|| {
info!("Did not find anything recursively. Falling back to default alias.");
default_version::find_default_version(config).map(UserVersion::Full)
})
}
}
}
fn get_user_version_for_directory_recursive(path: impl AsRef<Path>) -> Option<UserVersion> {
let mut current_path = Some(path.as_ref());
while let Some(child_path) = current_path {
if let Some(version) = get_user_version_for_single_directory(child_path) {
return Some(version);
}
current_path = child_path.parent();
}
None
}
pub fn get_user_version_for_single_directory(path: impl AsRef<Path>) -> Option<UserVersion> {
let path = path.as_ref();
for path_part in &PATH_PARTS {

Loading…
Cancel
Save