node-version-bridge 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marcos Reuquen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # node-version-bridge
2
+
3
+ Automatically detects the Node.js version declared in your project and applies it using your preferred version manager.
4
+
5
+ ## Problem
6
+
7
+ Many Node projects already have files like `.nvmrc` or `.node-version`, but if you use a different version manager than the rest of your team (asdf, nvm, fnm, mise, n), you end up maintaining duplicate files or running manual commands every time you switch projects.
8
+
9
+ ## Solution
10
+
11
+ `nvb` reads the version files that already exist in your project and automatically applies the correct Node version when you enter the directory — regardless of which version manager you use.
12
+
13
+ ## Supported managers
14
+
15
+ - **nvm** — `nvm use <version>`
16
+ - **fnm** — `fnm use <version>`
17
+ - **mise** — `mise shell node@<version>`
18
+ - **asdf** — `export ASDF_NODEJS_VERSION=<version>`
19
+ - **n** — `n <version>`
20
+
21
+ ## Detected version files
22
+
23
+ By default, in this priority order:
24
+
25
+ 1. `.nvmrc`
26
+ 2. `.node-version`
27
+ 3. `.tool-versions`
28
+ 4. `package.json` (`engines.node`)
29
+
30
+ Aliases like `lts/*`, `lts/iron`, `node`, `stable`, and `latest` are automatically resolved to concrete versions via the nodejs.org API.
31
+
32
+ ## Installation
33
+
34
+ ### Quick install (recommended)
35
+
36
+ ```bash
37
+ git clone https://github.com/marcosreuquen/node-version-bridge.git
38
+ cd node-version-bridge
39
+ bash install.sh
40
+ ```
41
+
42
+ The script will:
43
+ 1. Copy `nvb` to `~/.local/share/nvb/`
44
+ 2. Automatically add the shell hook to your `.zshrc` or `.bashrc`
45
+
46
+ Then restart your shell or run `source ~/.zshrc` (or `~/.bashrc`).
47
+
48
+ ### Manual install
49
+
50
+ If you prefer full control over the installation:
51
+
52
+ ```bash
53
+ git clone https://github.com/marcosreuquen/node-version-bridge.git
54
+ ```
55
+
56
+ Then add the hook to your shell config:
57
+
58
+ **Zsh** — add to `~/.zshrc`:
59
+
60
+ ```bash
61
+ source /path/to/node-version-bridge/hooks/nvb.zsh
62
+ ```
63
+
64
+ **Bash** — add to `~/.bashrc`:
65
+
66
+ ```bash
67
+ source /path/to/node-version-bridge/hooks/nvb.bash
68
+ ```
69
+
70
+ ### Verify installation
71
+
72
+ ```bash
73
+ nvb doctor
74
+ ```
75
+
76
+ ## Uninstall
77
+
78
+ ### With the script
79
+
80
+ From the repository directory:
81
+
82
+ ```bash
83
+ bash uninstall.sh
84
+ ```
85
+
86
+ This removes installed files, the shell hook from your config, and the cache.
87
+
88
+ ### Manual
89
+
90
+ 1. Remove the `source ...nvb.zsh` (or `nvb.bash`) line from your `.zshrc`/`.bashrc`
91
+ 2. Delete the install directory: `rm -rf ~/.local/share/nvb`
92
+ 3. Optional — delete the cache: `rm -rf ~/.cache/node-version-bridge`
93
+
94
+ ## Usage
95
+
96
+ ```bash
97
+ # Show resolved version vs active version
98
+ nvb current
99
+
100
+ # Full diagnostics
101
+ nvb doctor
102
+
103
+ # Manage configuration
104
+ nvb config list
105
+ nvb config set NVB_AUTO_INSTALL true
106
+
107
+ # Show help
108
+ nvb help
109
+ ```
110
+
111
+ ## Configuration
112
+
113
+ Configuration can be set via environment variables or a config file at `~/.config/nvb/config` (or `$XDG_CONFIG_HOME/nvb/config`). Environment variables always take precedence over the config file.
114
+
115
+ ### Config file
116
+
117
+ Create `~/.config/nvb/config` (or use `nvb config set`):
118
+
119
+ ```ini
120
+ NVB_MANAGER=fnm
121
+ NVB_LOG_LEVEL=info
122
+ NVB_AUTO_INSTALL=true
123
+ ```
124
+
125
+ ### Manage config from the CLI
126
+
127
+ ```bash
128
+ nvb config list # Show all config values and sources
129
+ nvb config set NVB_AUTO_INSTALL true # Enable auto-install
130
+ nvb config set NVB_MANAGER fnm # Force fnm
131
+ nvb config get NVB_LOG_LEVEL # Read a value
132
+ nvb config unset NVB_MANAGER # Remove a key
133
+ nvb config path # Show config file path
134
+ ```
135
+
136
+ ### Environment variables
137
+
138
+ | Variable | Description | Default |
139
+ |---|---|---|
140
+ | `NVB_MANAGER` | Force a specific version manager | auto-detect |
141
+ | `NVB_LOG_LEVEL` | Log level: error, warn, info, debug | `error` |
142
+ | `NVB_PRIORITY` | File priority (comma-separated) | `.nvmrc,.node-version,.tool-versions,package.json` |
143
+ | `NVB_CACHE_DIR` | Cache directory | `$XDG_CACHE_HOME/node-version-bridge` |
144
+ | `NVB_AUTO_INSTALL` | Auto-install missing Node versions: true/false | `false` |
145
+ | `NVB_ALIAS_CACHE_TTL` | Alias cache TTL in seconds | `3600` |
146
+
147
+ ## Tests
148
+
149
+ ```bash
150
+ bash test/run.sh
151
+ ```
152
+
153
+ ## Expected outcome
154
+
155
+ - Less daily friction when switching between projects.
156
+ - No need to commit manager-specific files when `.nvmrc`/`.node-version` already exists.
157
+ - Works with any popular Node version manager.
158
+
159
+ ---
160
+
161
+ ## Documentation
162
+
163
+ - [Product concept](./docs/concept.md)
164
+ - [Technical design](./docs/technical-design.md)
165
+ - [Roadmap](./docs/roadmap.md)
166
+ - [Implementation plan](./docs/implementation-plan.md)
167
+ - [Changelog](./CHANGELOG.md)
168
+
169
+ ## Status
170
+
171
+ v0.5.0 — partial version resolution for asdf/mise, config file support, and auto-install. See [Changelog](./CHANGELOG.md).
172
+
173
+ **[Full Documentation](https://marcosreuquen.github.io/node-version-bridge/)**
package/bin/nvb ADDED
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — CLI entrypoint
3
+ # Detects Node.js version from project files and generates commands
4
+ # to apply it via the user's preferred version manager.
5
+
6
+ set -euo pipefail
7
+
8
+ NVB_VERSION="0.5.0"
9
+
10
+ # Resolve script location (handles symlinks on macOS/Linux)
11
+ _nvb_resolve_root() {
12
+ local source="${BASH_SOURCE[0]}"
13
+ while [[ -L "$source" ]]; do
14
+ local dir
15
+ dir="$(cd -P "$(dirname "$source")" && pwd)"
16
+ source="$(readlink "$source")"
17
+ [[ "$source" != /* ]] && source="${dir}/${source}"
18
+ done
19
+ cd -P "$(dirname "$source")/.." && pwd
20
+ }
21
+ NVB_ROOT="$(_nvb_resolve_root)"
22
+
23
+ # Source libraries
24
+ source "${NVB_ROOT}/lib/log.sh"
25
+ source "${NVB_ROOT}/lib/config.sh"
26
+ source "${NVB_ROOT}/lib/detect.sh"
27
+ source "${NVB_ROOT}/lib/alias.sh"
28
+ source "${NVB_ROOT}/lib/parse.sh"
29
+ source "${NVB_ROOT}/lib/resolve.sh"
30
+ source "${NVB_ROOT}/lib/cache.sh"
31
+ source "${NVB_ROOT}/lib/manager.sh"
32
+
33
+ # Load configuration file (env vars take precedence)
34
+ nvb_config_load
35
+
36
+ # --- Commands ---
37
+
38
+ nvb_cmd_refresh() {
39
+ # shellcheck disable=SC2119
40
+ nvb_resolve_version || true
41
+ local resolved_version="${NVB_RESOLVED_VERSION:-}"
42
+ local resolved_source="${NVB_RESOLVED_SOURCE:-}"
43
+
44
+ if [[ -z "$resolved_version" ]]; then
45
+ nvb_log debug "No version file found, nothing to do"
46
+ return 0
47
+ fi
48
+
49
+ # Check cache — skip if nothing changed
50
+ if nvb_cache_is_current "$PWD" "$resolved_version" "$resolved_source"; then
51
+ nvb_log debug "Cache hit: ${resolved_version} from ${resolved_source}"
52
+ return 0
53
+ fi
54
+
55
+ local manager
56
+ manager="$(nvb_detect_manager)" || {
57
+ echo "echo '[nvb] No supported version manager found.' >&2"
58
+ echo "echo '[nvb] Install one of: nvm, fnm, mise, asdf, n' >&2"
59
+ echo "echo '[nvb] See: https://github.com/marcosreuquen/node-version-bridge#supported-managers' >&2"
60
+ return 1
61
+ }
62
+
63
+ nvb_log info "Switching to Node ${resolved_version} (from ${resolved_source}) via ${manager}"
64
+
65
+ # Resolve partial version (e.g. "18" → "18.20.8") for managers that need exact versions
66
+ local apply_version
67
+ apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version")"
68
+
69
+ # Auto-install if enabled
70
+ local auto_install
71
+ auto_install="$(nvb_config_get NVB_AUTO_INSTALL false)"
72
+ if [[ "$auto_install" == "true" ]]; then
73
+ # If version couldn't be resolved to installed, install it first
74
+ if [[ "$apply_version" == "$resolved_version" ]] && ! _nvb_is_full_version "$resolved_version"; then
75
+ nvb_log debug "Auto-install enabled, no installed match for partial version ${resolved_version}"
76
+ nvb_adapter_install_cmd "$manager" "$resolved_version"
77
+ # Re-resolve after install (next hook trigger will pick it up if eval'd install succeeds)
78
+ apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version")"
79
+ else
80
+ nvb_log debug "Auto-install enabled, emitting install command"
81
+ nvb_adapter_install_cmd "$manager" "$apply_version"
82
+ fi
83
+ fi
84
+
85
+ # Output eval-able command for the shell hook
86
+ nvb_adapter_apply_cmd "$manager" "$apply_version"
87
+
88
+ # Update cache
89
+ nvb_cache_update "$PWD" "$resolved_version" "$resolved_source"
90
+ }
91
+
92
+ nvb_cmd_current() {
93
+ # shellcheck disable=SC2119
94
+ nvb_resolve_version || true
95
+ local resolved_version="${NVB_RESOLVED_VERSION:-}"
96
+ local resolved_source="${NVB_RESOLVED_SOURCE:-}"
97
+
98
+ local active_version
99
+ active_version="$(node --version 2>/dev/null | sed 's/^v//')" || active_version="(not found)"
100
+
101
+ local manager
102
+ manager="$(nvb_detect_manager 2>/dev/null)" || manager="(none detected)"
103
+
104
+ # Resolve partial version to show what would actually be applied
105
+ local apply_version="${resolved_version}"
106
+ if [[ -n "$resolved_version" && "$manager" != "(none detected)" ]]; then
107
+ apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version" 2>/dev/null)" || apply_version="$resolved_version"
108
+ fi
109
+
110
+ echo "node-version-bridge v${NVB_VERSION}"
111
+ echo ""
112
+ echo " Active Node: ${active_version}"
113
+ if [[ -n "$resolved_version" && "$apply_version" != "$resolved_version" ]]; then
114
+ echo " Resolved: ${resolved_version} → ${apply_version}"
115
+ else
116
+ echo " Resolved: ${resolved_version:-(none)}"
117
+ fi
118
+ echo " Source: ${resolved_source:-(none)}"
119
+ echo " Manager: ${manager}"
120
+ }
121
+
122
+ nvb_cmd_doctor() {
123
+ echo "node-version-bridge v${NVB_VERSION} — diagnostics"
124
+ echo ""
125
+
126
+ # Check version managers
127
+ echo "Version managers:"
128
+ local managers=("nvm" "fnm" "mise" "asdf" "n")
129
+ local active_manager
130
+ active_manager="$(nvb_detect_manager 2>/dev/null)" || active_manager=""
131
+
132
+ for m in "${managers[@]}"; do
133
+ if nvb_adapter_available "$m"; then
134
+ if [[ "$m" == "$active_manager" ]]; then
135
+ echo " ● ${m} (active)"
136
+ else
137
+ echo " ✓ ${m}"
138
+ fi
139
+ else
140
+ echo " ✗ ${m}"
141
+ fi
142
+ done
143
+
144
+ echo ""
145
+
146
+ # Check version files
147
+ echo "Version files (from $PWD):"
148
+ local priority_str="${NVB_PRIORITY:-.nvmrc,.node-version,.tool-versions,package.json}"
149
+ IFS=',' read -ra priorities <<< "$priority_str"
150
+
151
+ for f in "${priorities[@]}"; do
152
+ f="$(echo "$f" | xargs)"
153
+ local found
154
+ found="$(nvb_detect_file "$f" 2>/dev/null)" || found=""
155
+ if [[ -n "$found" ]]; then
156
+ local ver
157
+ ver="$(nvb_parse_file "$f" "$found" 2>/dev/null)" || ver="(parse error)"
158
+ echo " ✓ ${found} → ${ver}"
159
+ else
160
+ echo " ✗ ${f} (not found)"
161
+ fi
162
+ done
163
+
164
+ echo ""
165
+
166
+ # Configuration
167
+ echo "Configuration:"
168
+ echo " NVB_MANAGER: ${NVB_MANAGER:-(auto-detect)}"
169
+ echo " NVB_LOG_LEVEL: ${NVB_LOG_LEVEL:-error}"
170
+ echo " NVB_PRIORITY: ${NVB_PRIORITY:-.nvmrc,.node-version,.tool-versions,package.json}"
171
+ echo " NVB_CACHE_DIR: ${NVB_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/node-version-bridge}"
172
+ echo " NVB_AUTO_INSTALL: $(nvb_config_get NVB_AUTO_INSTALL false)"
173
+ echo " NVB_ALIAS_CACHE_TTL: ${NVB_ALIAS_CACHE_TTL:-3600}"
174
+ echo " Config file: $(nvb_config_file)"
175
+
176
+ echo ""
177
+ local node_ver
178
+ node_ver="$(node --version 2>/dev/null)" || node_ver="(not found)"
179
+ echo "Active Node: ${node_ver}"
180
+ }
181
+
182
+ nvb_cmd_config() {
183
+ local subcmd="${1:-list}"
184
+ shift 2>/dev/null || true
185
+
186
+ case "$subcmd" in
187
+ list|ls)
188
+ nvb_config_list
189
+ ;;
190
+ get)
191
+ local key="${1:-}"
192
+ if [[ -z "$key" ]]; then
193
+ echo "Usage: nvb config get <KEY>" >&2
194
+ return 1
195
+ fi
196
+ nvb_config_get "$key"
197
+ ;;
198
+ set)
199
+ local key="${1:-}"
200
+ local value="${2:-}"
201
+ if [[ -z "$key" || -z "$value" ]]; then
202
+ echo "Usage: nvb config set <KEY> <VALUE>" >&2
203
+ return 1
204
+ fi
205
+ nvb_config_set "$key" "$value"
206
+ echo "Set ${key}=${value} in $(nvb_config_file)"
207
+ ;;
208
+ unset)
209
+ local key="${1:-}"
210
+ if [[ -z "$key" ]]; then
211
+ echo "Usage: nvb config unset <KEY>" >&2
212
+ return 1
213
+ fi
214
+ nvb_config_unset "$key"
215
+ echo "Removed ${key} from $(nvb_config_file)"
216
+ ;;
217
+ path)
218
+ nvb_config_file
219
+ ;;
220
+ *)
221
+ echo "Usage: nvb config <list|get|set|unset|path>" >&2
222
+ echo "" >&2
223
+ echo " list Show all config values and their sources" >&2
224
+ echo " get <KEY> Get a config value" >&2
225
+ echo " set <KEY> <VALUE> Set a config value" >&2
226
+ echo " unset <KEY> Remove a config value" >&2
227
+ echo " path Show config file path" >&2
228
+ return 1
229
+ ;;
230
+ esac
231
+ }
232
+
233
+ nvb_cmd_help() {
234
+ cat <<EOF
235
+ node-version-bridge v${NVB_VERSION}
236
+
237
+ Automatically applies the Node.js version declared in your project
238
+ using your preferred version manager.
239
+
240
+ Usage: nvb <command> [options]
241
+
242
+ Commands:
243
+ refresh Detect version and output shell commands to apply it (for eval)
244
+ current Show resolved version, source, and active Node version
245
+ doctor Check available managers and configuration
246
+ config Manage configuration (list, get, set, unset, path)
247
+ version Show nvb version
248
+ help Show this help
249
+
250
+ Config examples:
251
+ nvb config list Show all configuration
252
+ nvb config set NVB_AUTO_INSTALL true Enable auto-install
253
+ nvb config set NVB_MANAGER fnm Force fnm as manager
254
+ nvb config set NVB_LOG_LEVEL debug Enable debug logging
255
+ nvb config unset NVB_MANAGER Remove manager override
256
+
257
+ Supported version files: .nvmrc, .node-version, .tool-versions, package.json (engines.node)
258
+ Supported managers: nvm, fnm, mise, asdf, n
259
+
260
+ Shell integration (add to your shell config):
261
+ Zsh: source <path-to-nvb>/hooks/nvb.zsh
262
+ Bash: source <path-to-nvb>/hooks/nvb.bash
263
+
264
+ Environment variables:
265
+ NVB_MANAGER Force a specific version manager (nvm, fnm, mise, asdf, n)
266
+ NVB_LOG_LEVEL Log verbosity: error, warn, info, debug (default: error)
267
+ NVB_PRIORITY Comma-separated file priority (default: .nvmrc,.node-version,.tool-versions,package.json)
268
+ NVB_CACHE_DIR Override cache directory
269
+ NVB_AUTO_INSTALL Auto-install missing versions: true/false (default: false)
270
+ NVB_ALIAS_CACHE_TTL Alias cache TTL in seconds (default: 3600)
271
+
272
+ Configuration file: $(nvb_config_file)
273
+ EOF
274
+ }
275
+
276
+ # --- Main dispatch ---
277
+
278
+ case "${1:-help}" in
279
+ refresh) nvb_cmd_refresh ;;
280
+ current) nvb_cmd_current ;;
281
+ doctor) nvb_cmd_doctor ;;
282
+ config) shift; nvb_cmd_config "$@" ;;
283
+ version|-v) echo "nvb ${NVB_VERSION}" ;;
284
+ help|--help|-h) nvb_cmd_help ;;
285
+ *)
286
+ nvb_log error "Unknown command: $1"
287
+ nvb_cmd_help >&2
288
+ exit 1
289
+ ;;
290
+ esac
package/hooks/nvb.bash ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Bash hook
3
+ # Source this file in your .bashrc
4
+
5
+ # Resolve nvb binary path relative to this hook file
6
+ _NVB_BIN="$(cd "$(dirname "${BASH_SOURCE[0]}")/../bin" && pwd)/nvb"
7
+
8
+ _nvb_hook() {
9
+ # Skip if directory hasn't changed (fast path, avoids fork)
10
+ [[ "${_NVB_LAST_DIR:-}" == "$PWD" ]] && return
11
+ _NVB_LAST_DIR="$PWD"
12
+ eval "$("${_NVB_BIN}" refresh 2>/dev/null)"
13
+ }
14
+
15
+ # Append to PROMPT_COMMAND without duplicating
16
+ if [[ ";${PROMPT_COMMAND:-};" != *";_nvb_hook;"* ]]; then
17
+ PROMPT_COMMAND="_nvb_hook;${PROMPT_COMMAND:-}"
18
+ fi
package/hooks/nvb.zsh ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env zsh
2
+ # node-version-bridge — Zsh hook
3
+ # Source this file in your .zshrc
4
+
5
+ # Resolve nvb binary path relative to this hook file
6
+ _NVB_BIN="${0:A:h}/../bin/nvb"
7
+
8
+ _nvb_hook() {
9
+ # Skip if directory hasn't changed (fast path)
10
+ [[ "${_NVB_LAST_DIR:-}" == "$PWD" ]] && return
11
+ _NVB_LAST_DIR="$PWD"
12
+ eval "$("${_NVB_BIN}" refresh 2>/dev/null)"
13
+ }
14
+
15
+ autoload -U add-zsh-hook
16
+ add-zsh-hook chpwd _nvb_hook
17
+
18
+ # Run once on shell start
19
+ _nvb_hook
package/lib/alias.sh ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Alias resolution
3
+ # Resolves Node.js aliases (lts/*, node, stable) to concrete versions
4
+ # using the Node.js release schedule API.
5
+
6
+ NVB_ALIAS_CACHE_TTL="${NVB_ALIAS_CACHE_TTL:-3600}" # 1 hour default
7
+ _NVB_NODE_INDEX_URL="https://nodejs.org/dist/index.json"
8
+
9
+ # Get the alias cache file path
10
+ _nvb_alias_cache_file() {
11
+ echo "${NVB_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/node-version-bridge}/node-index.json"
12
+ }
13
+
14
+ # Fetch the Node.js version index (with caching)
15
+ _nvb_fetch_node_index() {
16
+ local cache_file
17
+ cache_file="$(_nvb_alias_cache_file)"
18
+
19
+ # Check if cache is fresh
20
+ if [[ -f "$cache_file" ]]; then
21
+ local mtime now age
22
+ if stat -f '%m' "$cache_file" &>/dev/null; then
23
+ # macOS
24
+ mtime="$(stat -f '%m' "$cache_file")"
25
+ else
26
+ # Linux
27
+ mtime="$(stat -c '%Y' "$cache_file")"
28
+ fi
29
+ now="$(date +%s)"
30
+ age=$(( now - mtime ))
31
+ if (( age < NVB_ALIAS_CACHE_TTL )); then
32
+ nvb_log debug "Using cached node index (age: ${age}s)"
33
+ cat "$cache_file"
34
+ return 0
35
+ fi
36
+ fi
37
+
38
+ nvb_log debug "Fetching node index from ${_NVB_NODE_INDEX_URL}"
39
+
40
+ local content
41
+ if command -v curl &>/dev/null; then
42
+ content="$(curl -sL --max-time 10 "$_NVB_NODE_INDEX_URL" 2>/dev/null)"
43
+ elif command -v wget &>/dev/null; then
44
+ content="$(wget -qO- --timeout=10 "$_NVB_NODE_INDEX_URL" 2>/dev/null)"
45
+ else
46
+ nvb_log warn "Neither curl nor wget available, cannot resolve aliases"
47
+ return 1
48
+ fi
49
+
50
+ if [[ -z "$content" ]]; then
51
+ nvb_log warn "Failed to fetch node index"
52
+ # Fall back to stale cache if it exists
53
+ if [[ -f "$cache_file" ]]; then
54
+ nvb_log debug "Falling back to stale cache"
55
+ cat "$cache_file"
56
+ return 0
57
+ fi
58
+ return 1
59
+ fi
60
+
61
+ mkdir -p "$(dirname "$cache_file")"
62
+ echo "$content" > "$cache_file"
63
+ echo "$content"
64
+ }
65
+
66
+ # Resolve an alias to a concrete version
67
+ # Supported: lts/*, lts, lts/<codename>, node, stable, latest
68
+ nvb_resolve_alias() {
69
+ local alias="$1"
70
+
71
+ # Normalize
72
+ alias="$(echo "$alias" | tr '[:upper:]' '[:lower:]')"
73
+
74
+ local index
75
+ index="$(_nvb_fetch_node_index)" || return 1
76
+
77
+ local version=""
78
+
79
+ case "$alias" in
80
+ lts|lts/\*)
81
+ # Latest LTS version
82
+ version="$(_nvb_extract_latest_lts "$index")"
83
+ ;;
84
+ lts/*)
85
+ # Specific LTS codename (e.g., lts/iron, lts/hydrogen)
86
+ local codename="${alias#lts/}"
87
+ version="$(_nvb_extract_lts_by_codename "$index" "$codename")"
88
+ ;;
89
+ node|stable|latest)
90
+ # Latest current version
91
+ version="$(_nvb_extract_latest "$index")"
92
+ ;;
93
+ *)
94
+ return 1
95
+ ;;
96
+ esac
97
+
98
+ if [[ -n "$version" ]]; then
99
+ nvb_log debug "Resolved alias '${alias}' → ${version}"
100
+ echo "$version"
101
+ return 0
102
+ fi
103
+
104
+ nvb_log warn "Could not resolve alias '${alias}'"
105
+ return 1
106
+ }
107
+
108
+ # Extract the latest LTS version from the index
109
+ _nvb_extract_latest_lts() {
110
+ local index="$1"
111
+ # index.json: array of objects with "version":"v22.x.x","lts":"Jod" or "lts":false
112
+ # First entry with lts !== false is the latest LTS
113
+ echo "$index" | grep -o '"version":"v[^"]*","date":"[^"]*","files":\[[^]]*\],"npm":"[^"]*","v8":"[^"]*","uv":"[^"]*","zlib":"[^"]*","openssl":"[^"]*","modules":"[^"]*","lts":"[^"]*"' \
114
+ | grep '"lts":"[^f]' | head -1 | grep -o '"version":"v[^"]*"' | head -1 | sed 's/"version":"v\([^"]*\)"/\1/'
115
+ }
116
+
117
+ # Extract LTS version by codename
118
+ _nvb_extract_lts_by_codename() {
119
+ local index="$1"
120
+ local codename="$2"
121
+ # Case-insensitive match on codename
122
+ echo "$index" | grep -oi "\"version\":\"v[^\"]*\"[^}]*\"lts\":\"${codename}\"" \
123
+ | head -1 | grep -o '"version":"v[^"]*"' | sed 's/"version":"v\([^"]*\)"/\1/'
124
+ }
125
+
126
+ # Extract the latest (current) version
127
+ _nvb_extract_latest() {
128
+ local index="$1"
129
+ echo "$index" | grep -o '"version":"v[^"]*"' | head -1 | sed 's/"version":"v\([^"]*\)"/\1/'
130
+ }
131
+
132
+ # Check if a string looks like a known alias
133
+ nvb_is_alias() {
134
+ local value="$1"
135
+ value="$(echo "$value" | tr '[:upper:]' '[:lower:]')"
136
+ case "$value" in
137
+ lts|lts/*|node|stable|latest) return 0 ;;
138
+ *) return 1 ;;
139
+ esac
140
+ }
package/lib/cache.sh ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Cache management
3
+ # Avoids redundant version switches when directory/version hasn't changed
4
+
5
+ NVB_CACHE_DIR="${NVB_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/node-version-bridge}"
6
+
7
+ nvb_cache_file() {
8
+ echo "${NVB_CACHE_DIR}/state"
9
+ }
10
+
11
+ # Check if cached state matches current context
12
+ nvb_cache_is_current() {
13
+ local cwd="$1" version="$2" source="$3"
14
+ local cache_file
15
+ cache_file="$(nvb_cache_file)"
16
+
17
+ [[ -f "$cache_file" ]] || return 1
18
+
19
+ local cached_cwd cached_version cached_source
20
+ cached_cwd="$(sed -n '1p' "$cache_file")"
21
+ cached_version="$(sed -n '2p' "$cache_file")"
22
+ cached_source="$(sed -n '3p' "$cache_file")"
23
+
24
+ [[ "$cached_cwd" == "$cwd" && "$cached_version" == "$version" && "$cached_source" == "$source" ]]
25
+ }
26
+
27
+ # Update cache with current state
28
+ nvb_cache_update() {
29
+ local cwd="$1" version="$2" source="$3"
30
+ local cache_file
31
+ cache_file="$(nvb_cache_file)"
32
+
33
+ mkdir -p "$(dirname "$cache_file")"
34
+ printf '%s\n%s\n%s\n' "$cwd" "$version" "$source" > "$cache_file"
35
+ }
36
+
37
+ # Clear stored cache
38
+ nvb_cache_clear() {
39
+ local cache_file
40
+ cache_file="$(nvb_cache_file)"
41
+ rm -f "$cache_file"
42
+ }
package/lib/config.sh ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Configuration file support
3
+ # Loads and manages config from ~/.config/nvb/config
4
+
5
+ nvb_config_dir() {
6
+ echo "${XDG_CONFIG_HOME:-$HOME/.config}/nvb"
7
+ }
8
+
9
+ nvb_config_file() {
10
+ echo "$(nvb_config_dir)/config"
11
+ }
12
+
13
+ # Load config file (environment variables take precedence)
14
+ nvb_config_load() {
15
+ local config_file
16
+ config_file="$(nvb_config_file)"
17
+ [[ -f "$config_file" ]] || return 0
18
+
19
+ local key value
20
+ while IFS='=' read -r key value || [[ -n "$key" ]]; do
21
+ key="$(echo "$key" | xargs)" 2>/dev/null || continue
22
+ [[ -z "$key" || "$key" == \#* ]] && continue
23
+ value="$(echo "$value" | sed "s/^[\"']//;s/[\"']$//")"
24
+ if [[ -z "${!key:-}" ]]; then
25
+ export "$key=$value"
26
+ fi
27
+ done < "$config_file"
28
+ }
29
+
30
+ # Get a config value (env > file > default)
31
+ nvb_config_get() {
32
+ local key="$1"
33
+ local default="${2:-}"
34
+
35
+ if [[ -n "${!key:-}" ]]; then
36
+ echo "${!key}"
37
+ return 0
38
+ fi
39
+
40
+ local config_file
41
+ config_file="$(nvb_config_file)"
42
+ if [[ -f "$config_file" ]]; then
43
+ local file_value
44
+ file_value="$(grep "^${key}=" "$config_file" 2>/dev/null | tail -1 | cut -d'=' -f2- | sed "s/^[\"']//;s/[\"']$//")"
45
+ if [[ -n "$file_value" ]]; then
46
+ echo "$file_value"
47
+ return 0
48
+ fi
49
+ fi
50
+
51
+ echo "$default"
52
+ }
53
+
54
+ # Set a config value in the file
55
+ nvb_config_set() {
56
+ local key="$1"
57
+ local value="$2"
58
+ local config_dir config_file
59
+
60
+ config_dir="$(nvb_config_dir)"
61
+ config_file="$(nvb_config_file)"
62
+
63
+ mkdir -p "$config_dir"
64
+
65
+ if [[ -f "$config_file" ]] && grep -q "^${key}=" "$config_file" 2>/dev/null; then
66
+ local tmp="${config_file}.tmp"
67
+ sed "s|^${key}=.*|${key}=${value}|" "$config_file" > "$tmp" && mv "$tmp" "$config_file"
68
+ else
69
+ echo "${key}=${value}" >> "$config_file"
70
+ fi
71
+ }
72
+
73
+ # Remove a config key
74
+ nvb_config_unset() {
75
+ local key="$1"
76
+ local config_file
77
+ config_file="$(nvb_config_file)"
78
+ [[ -f "$config_file" ]] || return 0
79
+
80
+ local tmp="${config_file}.tmp"
81
+ grep -v "^${key}=" "$config_file" > "$tmp" && mv "$tmp" "$config_file"
82
+ }
83
+
84
+ # List all config with source info
85
+ nvb_config_list() {
86
+ local config_file
87
+ config_file="$(nvb_config_file)"
88
+
89
+ echo "Configuration ($(nvb_config_file)):"
90
+ echo ""
91
+
92
+ local keys=("NVB_MANAGER" "NVB_LOG_LEVEL" "NVB_PRIORITY" "NVB_CACHE_DIR" "NVB_ALIAS_CACHE_TTL" "NVB_AUTO_INSTALL")
93
+ for key in "${keys[@]}"; do
94
+ local env_val="${!key:-}"
95
+ local file_val=""
96
+ local effective=""
97
+ local source=""
98
+
99
+ if [[ -f "$config_file" ]]; then
100
+ file_val="$(grep "^${key}=" "$config_file" 2>/dev/null | tail -1 | cut -d'=' -f2- | sed "s/^[\"']//;s/[\"']$//")" || true
101
+ fi
102
+
103
+ if [[ -n "$env_val" ]]; then
104
+ effective="$env_val"
105
+ if [[ -n "$file_val" ]]; then
106
+ source="env (overrides file: ${file_val})"
107
+ else
108
+ source="env"
109
+ fi
110
+ elif [[ -n "$file_val" ]]; then
111
+ effective="$file_val"
112
+ source="config file"
113
+ else
114
+ effective="(default)"
115
+ source=""
116
+ fi
117
+
118
+ if [[ -n "$source" ]]; then
119
+ echo " ${key}=${effective} <- ${source}"
120
+ else
121
+ echo " ${key}=${effective}"
122
+ fi
123
+ done
124
+ }
package/lib/detect.sh ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Version file detection
3
+ # Walks up directory tree to find version declaration files
4
+
5
+ # Find a specific file walking up from a directory
6
+ # Usage: nvb_detect_file <filename> [start_dir]
7
+ nvb_detect_file() {
8
+ local filename="$1"
9
+ local dir="${2:-$PWD}"
10
+
11
+ while [[ "$dir" != "/" ]]; do
12
+ if [[ -f "${dir}/${filename}" ]]; then
13
+ echo "${dir}/${filename}"
14
+ return 0
15
+ fi
16
+ dir="$(dirname "$dir")"
17
+ done
18
+
19
+ # Check filesystem root
20
+ if [[ -f "/${filename}" ]]; then
21
+ echo "/${filename}"
22
+ return 0
23
+ fi
24
+
25
+ return 1
26
+ }
package/lib/log.sh ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Logging utilities
3
+ # All log output goes to stderr to keep stdout clean for eval-able commands
4
+
5
+ nvb_log() {
6
+ local level="$1"
7
+ shift
8
+ local configured="${NVB_LOG_LEVEL:-error}"
9
+
10
+ local level_num configured_num
11
+ level_num=$(_nvb_log_level_num "$level")
12
+ configured_num=$(_nvb_log_level_num "$configured")
13
+
14
+ if (( level_num <= configured_num )); then
15
+ echo "[nvb:${level}] $*" >&2
16
+ fi
17
+ }
18
+
19
+ _nvb_log_level_num() {
20
+ case "$1" in
21
+ error) echo 0 ;;
22
+ warn) echo 1 ;;
23
+ info) echo 2 ;;
24
+ debug) echo 3 ;;
25
+ *) echo 0 ;;
26
+ esac
27
+ }
package/lib/manager.sh ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Manager detection and adapter dispatch
3
+ # Detects the active Node version manager and generates apply commands
4
+
5
+ # Check if a version is a full semver (x.y.z)
6
+ _nvb_is_full_version() {
7
+ [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
8
+ }
9
+
10
+ # Resolve a partial version (e.g. "18" or "18.20") to the best matching
11
+ # installed version. For managers that handle partials natively (nvm, fnm, n),
12
+ # returns the version as-is. For asdf/mise, queries installed versions.
13
+ nvb_resolve_installed_version() {
14
+ local manager="$1"
15
+ local version="$2"
16
+
17
+ # Full semver doesn't need resolution
18
+ if _nvb_is_full_version "$version"; then
19
+ echo "$version"
20
+ return 0
21
+ fi
22
+
23
+ local match=""
24
+ case "$manager" in
25
+ asdf)
26
+ match="$(asdf list nodejs 2>/dev/null | tr -d ' *' | grep -E "^${version}(\.|$)" | sort -t. -k1,1n -k2,2n -k3,3n | tail -1)"
27
+ ;;
28
+ mise)
29
+ match="$(mise ls --installed node 2>/dev/null | awk '{print $2}' | grep -E "^${version}(\.|$)" | sort -t. -k1,1n -k2,2n -k3,3n | tail -1)"
30
+ ;;
31
+ *)
32
+ # nvm, fnm, n handle partial versions natively
33
+ echo "$version"
34
+ return 0
35
+ ;;
36
+ esac
37
+
38
+ if [[ -n "$match" ]]; then
39
+ nvb_log debug "Resolved partial version ${version} → ${match} for ${manager}"
40
+ echo "$match"
41
+ else
42
+ nvb_log debug "No installed version matching ${version} for ${manager}"
43
+ echo "$version"
44
+ fi
45
+ }
46
+
47
+ # Detect which version manager is available
48
+ # Respects NVB_MANAGER override, otherwise auto-detects
49
+ nvb_detect_manager() {
50
+ local manager="${NVB_MANAGER:-}"
51
+ if [[ -n "$manager" ]]; then
52
+ nvb_log debug "Using configured manager: ${manager}"
53
+ echo "$manager"
54
+ return 0
55
+ fi
56
+
57
+ # Auto-detect in common preference order
58
+ if [[ -n "${NVM_DIR:-}" ]]; then nvb_log debug "Detected nvm"; echo "nvm"; return 0; fi
59
+ if command -v fnm &>/dev/null; then nvb_log debug "Detected fnm"; echo "fnm"; return 0; fi
60
+ if command -v mise &>/dev/null; then nvb_log debug "Detected mise"; echo "mise"; return 0; fi
61
+ if command -v asdf &>/dev/null; then nvb_log debug "Detected asdf"; echo "asdf"; return 0; fi
62
+ if command -v n &>/dev/null; then nvb_log debug "Detected n"; echo "n"; return 0; fi
63
+
64
+ nvb_log error "No supported version manager detected"
65
+ return 1
66
+ }
67
+
68
+ # Check if a specific manager is available
69
+ nvb_adapter_available() {
70
+ local manager="$1"
71
+ case "$manager" in
72
+ nvm) [[ -n "${NVM_DIR:-}" ]] ;;
73
+ fnm) command -v fnm &>/dev/null ;;
74
+ mise) command -v mise &>/dev/null ;;
75
+ asdf) command -v asdf &>/dev/null ;;
76
+ n) command -v n &>/dev/null ;;
77
+ *) return 1 ;;
78
+ esac
79
+ }
80
+
81
+ # Output eval-able shell command to switch Node version
82
+ # The output is designed to be eval'd in the user's interactive shell
83
+ nvb_adapter_apply_cmd() {
84
+ local manager="$1"
85
+ local version="$2"
86
+
87
+ case "$manager" in
88
+ nvm)
89
+ echo "nvm use '${version}' >/dev/null 2>&1 || echo '[nvb] Node ${version} not installed via nvm. Run: nvm install ${version}' >&2"
90
+ ;;
91
+ fnm)
92
+ echo "fnm use '${version}' --log-level quiet 2>/dev/null || echo '[nvb] Node ${version} not installed via fnm. Run: fnm install ${version}' >&2"
93
+ ;;
94
+ mise)
95
+ echo "eval \"\$(mise shell node@'${version}' 2>/dev/null)\" 2>/dev/null || echo '[nvb] Node ${version} not available via mise. Run: mise install node@${version}' >&2"
96
+ ;;
97
+ asdf)
98
+ echo "export ASDF_NODEJS_VERSION='${version}'"
99
+ ;;
100
+ n)
101
+ echo "n '${version}' >/dev/null 2>&1 || echo '[nvb] Node ${version} not installed via n. Run: n install ${version}' >&2"
102
+ ;;
103
+ *)
104
+ nvb_log error "Unknown manager: ${manager}"
105
+ return 1
106
+ ;;
107
+ esac
108
+ }
109
+
110
+ # Output eval-able shell command to install a Node version
111
+ nvb_adapter_install_cmd() {
112
+ local manager="$1"
113
+ local version="$2"
114
+
115
+ case "$manager" in
116
+ nvm)
117
+ echo "nvm install '${version}' >/dev/null 2>&1"
118
+ ;;
119
+ fnm)
120
+ echo "fnm install '${version}' --log-level quiet 2>/dev/null"
121
+ ;;
122
+ mise)
123
+ echo "mise install node@'${version}' >/dev/null 2>&1"
124
+ ;;
125
+ asdf)
126
+ if _nvb_is_full_version "$version"; then
127
+ echo "asdf install nodejs '${version}' >/dev/null 2>&1"
128
+ else
129
+ echo "asdf install nodejs \"\$(asdf latest nodejs '${version}')\" >/dev/null 2>&1"
130
+ fi
131
+ ;;
132
+ n)
133
+ echo "n '${version}' >/dev/null 2>&1"
134
+ ;;
135
+ *)
136
+ nvb_log error "Unknown manager: ${manager}"
137
+ return 1
138
+ ;;
139
+ esac
140
+ }
package/lib/parse.sh ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Version file parsing
3
+ # Extracts and normalizes Node.js version from various file formats
4
+
5
+ # Dispatch parsing based on file type
6
+ nvb_parse_file() {
7
+ local filename="$1"
8
+ local filepath="$2"
9
+
10
+ case "$(basename "$filename")" in
11
+ .nvmrc) nvb_parse_nvmrc "$filepath" ;;
12
+ .node-version) nvb_parse_node_version "$filepath" ;;
13
+ .tool-versions) nvb_parse_tool_versions "$filepath" ;;
14
+ package.json) nvb_parse_package_json "$filepath" ;;
15
+ *)
16
+ nvb_log warn "Unknown file format: ${filename}"
17
+ return 1
18
+ ;;
19
+ esac
20
+ }
21
+
22
+ # Parse .nvmrc — strips leading 'v', validates semver-like pattern
23
+ nvb_parse_nvmrc() {
24
+ local filepath="$1"
25
+ local content
26
+ content="$(head -1 "$filepath" 2>/dev/null | tr -d '[:space:]')"
27
+
28
+ [[ -z "$content" ]] && return 1
29
+
30
+ # Strip leading 'v' or 'V'
31
+ content="${content#v}"
32
+ content="${content#V}"
33
+
34
+ # Resolve aliases (lts/*, node, stable, etc.)
35
+ if nvb_is_alias "$content"; then
36
+ local resolved
37
+ resolved="$(nvb_resolve_alias "$content")" || {
38
+ nvb_log warn "Could not resolve alias '${content}' in ${filepath} (network unavailable?)"
39
+ return 1
40
+ }
41
+ echo "$resolved"
42
+ return 0
43
+ fi
44
+
45
+ # Accept partial or full semver: 18, 18.19, 18.19.0
46
+ if [[ "$content" =~ ^[0-9]+(\.[0-9]+)?(\.[0-9]+)?$ ]]; then
47
+ echo "$content"
48
+ return 0
49
+ fi
50
+
51
+ nvb_log warn "Could not parse version from ${filepath}: '${content}'"
52
+ return 1
53
+ }
54
+
55
+ # Parse .node-version — same format rules as .nvmrc
56
+ nvb_parse_node_version() {
57
+ nvb_parse_nvmrc "$1"
58
+ }
59
+
60
+ # Parse .tool-versions — extract 'nodejs' or 'node' entry
61
+ nvb_parse_tool_versions() {
62
+ local filepath="$1"
63
+ local version
64
+
65
+ version="$(grep -E '^(nodejs|node)\s+' "$filepath" 2>/dev/null | head -1 | awk '{print $2}' | tr -d '[:space:]')"
66
+
67
+ [[ -z "$version" ]] && return 1
68
+
69
+ version="${version#v}"
70
+ version="${version#V}"
71
+
72
+ if [[ "$version" =~ ^[0-9]+(\.[0-9]+)?(\.[0-9]+)?$ ]]; then
73
+ echo "$version"
74
+ return 0
75
+ fi
76
+
77
+ nvb_log warn "Could not parse nodejs version from ${filepath}: '${version}'"
78
+ return 1
79
+ }
80
+
81
+ # Parse package.json — extract engines.node field
82
+ nvb_parse_package_json() {
83
+ local filepath="$1"
84
+
85
+ [[ -f "$filepath" ]] || return 1
86
+
87
+ local engines_node
88
+ # Extract "engines":{..."node":"<value>"...} using grep/sed (no jq dependency)
89
+ engines_node="$(grep -o '"engines"[[:space:]]*:[[:space:]]*{[^}]*}' "$filepath" 2>/dev/null \
90
+ | grep -o '"node"[[:space:]]*:[[:space:]]*"[^"]*"' \
91
+ | head -1 \
92
+ | sed 's/"node"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')"
93
+
94
+ [[ -z "$engines_node" ]] && return 1
95
+
96
+ nvb_log debug "Found engines.node: '${engines_node}' in ${filepath}"
97
+
98
+ # Handle exact versions: "18.19.0", "v18.19.0", "18"
99
+ local clean="$engines_node"
100
+ clean="${clean#v}"
101
+ clean="${clean#V}"
102
+
103
+ if [[ "$clean" =~ ^[0-9]+(\.[0-9]+)?(\.[0-9]+)?$ ]]; then
104
+ echo "$clean"
105
+ return 0
106
+ fi
107
+
108
+ # Handle semver ranges: extract the base version
109
+ # ">=18.0.0", "^18.19.0", "~18.19.0", ">=18"
110
+ local base
111
+ base="$(echo "$clean" | sed 's/^[^0-9]*//' | grep -oE '^[0-9]+(\.[0-9]+)?(\.[0-9]+)?')"
112
+
113
+ if [[ -n "$base" ]]; then
114
+ nvb_log info "Interpreted engines.node '${engines_node}' as minimum version ${base}"
115
+ echo "$base"
116
+ return 0
117
+ fi
118
+
119
+ nvb_log warn "Could not parse engines.node from ${filepath}: '${engines_node}'"
120
+ return 1
121
+ }
package/lib/resolve.sh ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # node-version-bridge — Version resolution
3
+ # Applies priority rules to select the target Node.js version
4
+
5
+ # Resolve the target version from available files
6
+ # Sets: NVB_RESOLVED_VERSION, NVB_RESOLVED_SOURCE
7
+ nvb_resolve_version() {
8
+ local dir="${1:-$PWD}"
9
+ local priority_str="${NVB_PRIORITY:-.nvmrc,.node-version,.tool-versions,package.json}"
10
+
11
+ NVB_RESOLVED_VERSION=""
12
+ NVB_RESOLVED_SOURCE=""
13
+
14
+ IFS=',' read -ra priorities <<< "$priority_str"
15
+
16
+ for filename in "${priorities[@]}"; do
17
+ filename="$(echo "$filename" | xargs)" # trim whitespace
18
+ local filepath
19
+ filepath="$(nvb_detect_file "$filename" "$dir")" || continue
20
+
21
+ local version
22
+ version="$(nvb_parse_file "$filename" "$filepath")" || continue
23
+
24
+ export NVB_RESOLVED_VERSION="$version"
25
+ export NVB_RESOLVED_SOURCE="$filepath"
26
+ nvb_log debug "Resolved ${version} from ${filepath}"
27
+ return 0
28
+ done
29
+
30
+ nvb_log debug "No version file found from ${dir}"
31
+ return 1
32
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "node-version-bridge",
3
+ "version": "0.5.0",
4
+ "description": "Detect Node.js version from project files and apply it via any version manager",
5
+ "bin": {
6
+ "nvb": "bin/nvb"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "lib/",
11
+ "hooks/"
12
+ ],
13
+ "keywords": [
14
+ "node",
15
+ "version",
16
+ "nvm",
17
+ "fnm",
18
+ "mise",
19
+ "asdf",
20
+ "nvmrc"
21
+ ],
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/Marcosreuquen/node-version-bridge.git"
26
+ },
27
+ "author": "Marcos Reuquen",
28
+ "os": [
29
+ "darwin",
30
+ "linux"
31
+ ],
32
+ "engines": {
33
+ "node": ">=14"
34
+ }
35
+ }