Last active
June 1, 2025 02:52
-
-
Save bugficks/0df37ebd3790e080ce6722a329245669 to your computer and use it in GitHub Desktop.
clink (https://chrisant996.github.io/clink/) plugin for fnm (https://github.com/Schniz/fnm)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| if (clink.version_encoded or 0) < 10020030 then | |
| error("fnm requires a newer version of Clink; please upgrade to Clink v1.2.30 or later.") | |
| end | |
| -- ANSI escape codes for colors and styles | |
| local RED = "\27[31m" | |
| local YELLOW = "\27[33m" | |
| local CYAN = "\27[36m" | |
| local RESET = "\27[0m" | |
| local BOLD = "\27[1m" | |
| local ITAL = "\27[3m" | |
| local function prompt_install() | |
| local handle = io.popen("fnm use --silent-if-unchanged 2>&1") | |
| local t = handle:read("*a") | |
| handle:close() | |
| if not t or t == "" then return end | |
| -- Check for missing version error | |
| local version = t:match("error: Requested version ([^%s]+) is not currently installed") | |
| if version then | |
| version = tostring(version) | |
| io.write( | |
| RED, "Can't find an installed Node version matching ", ITAL, version, RESET, ".\n" | |
| ) | |
| io.write( | |
| YELLOW, "Do you want to install it? ", BOLD, "answer", RESET, YELLOW, " [y/N]: ", RESET | |
| ) | |
| local answer = io.read() | |
| if answer and answer:lower() == "y" then | |
| os.execute("fnm use --silent-if-unchanged --install-if-missing") | |
| end | |
| return | |
| end | |
| -- Success: output contains "Using Node v..." | |
| local node_version = t:match("Using Node (v%d+%.%d+%.%d+)") | |
| if node_version then | |
| io.write("Using Node ", CYAN, node_version, RESET, "\n") | |
| return | |
| end | |
| -- All other cases are errors | |
| error("fnm use --silent-if-unchanged failed: " .. t) | |
| end | |
| local function parse_fnm_env() | |
| local handle = io.popen('fnm env --use-on-cd 2>nul') | |
| if not handle then return nil end | |
| local out = handle:read("*a") | |
| handle:close() | |
| local env = {} | |
| for line in out:gmatch("[^\r\n]+") do | |
| -- Matches: set VAR=VALUE (quotes rarely used, but handle if present) | |
| local var, value = line:match("^[Ss][Ee][Tt]%s+([^=]+)=(.*)$") | |
| if var and value then | |
| value = value:gsub('^"(.*)"$', '%1') -- remove surrounding quotes if present | |
| env[var:match("^%s*(.-)%s*$")] = value -- trim spaces from var | |
| end | |
| end | |
| return env, out | |
| end | |
| local function check_and_use_fnm() | |
| -- Check FNM_VERSION_FILE_STRATEGY environment variable | |
| local fnm_strategy = os.getenv("FNM_VERSION_FILE_STRATEGY") | |
| -- -- Check if we should use recursive strategy | |
| if fnm_strategy == "recursive" then | |
| prompt_install() | |
| else | |
| -- Check for .nvmrc file | |
| local nvmrc = io.open(".nvmrc", "r") | |
| if nvmrc ~= nil then | |
| io.close(nvmrc) | |
| prompt_install() | |
| else | |
| -- Check for .node-version file | |
| local node_version = io.open(".node-version", "r") | |
| if node_version ~= nil then | |
| io.close(node_version) | |
| prompt_install() | |
| end | |
| end | |
| end | |
| end | |
| local function setup_fnm() | |
| local env, raw = parse_fnm_env() | |
| if not env or not raw then return end | |
| for var, value in pairs(env) do | |
| os.setenv(var, value) | |
| end | |
| -- doskey replacement | |
| clink.onbeginedit(check_and_use_fnm) | |
| -- remove fnm symlinks on exit | |
| clink.onendedit(function(line) | |
| if line:match("^exit") then | |
| local path = os.getenv("FNM_MULTISHELL_PATH") | |
| if path then | |
| os.rmdir(path) | |
| end | |
| end | |
| end) | |
| end | |
| setup_fnm() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment