-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnunvm.nu
339 lines (303 loc) · 9.66 KB
/
nunvm.nu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#!/usr/bin/env nu
# nunvm - Nushell Node Version Manager
# Implementing nvm in nushell
#
# To use, source this file in your config.nu file
# For using with non-nushell shells, create a separate
# script file which invokes it with nu
#
# Copyright 2023 Yakiyo. All rights reserved. MIT license.
use std log
(_nunvm_set_log_level)
# nunvm version
const nunvm_version = "0.1.0"
# Nodejs dist url
const node_dist = "https://nodejs.org/dist"
# Nodejs version manager in nushell
#
# https://github.com/Yakiyo/nunvm
def main [
--version(-v) # Display current nunvm version
] {
if $version {
print $"v($nunvm_version)"
return
}
help main
}
# Install a version of nodejs
def "main install" [
version?: string # The version to install
--lts # Install lts version
--latest # Install latest version
] {
# resolve version
let version: string = if $version == null and $lts == false and $latest == true {
let v = (_nunvm_get_latest)
log info $"Resolving version latest to (ansi blue)($v)(ansi reset)"
$v
} else if $version == null and $lts == true and $latest == false {
let v = (_nunvm_get_lts)
log info $"Resolving version lts to (ansi blue)($v)(ansi reset)"
$v
} else if $version != null and $lts == false and $latest == false {
if not (_nunvm_is_valid_version $version) {
_nunvm_throw "E_INVALID_VERSION" $"($version) is not a valid string"
}
let v = (_nunvm_prepend_version $"($version)")
$v
} else if ([$env.PWD ".nvmrc"] | path join | path exists) {
let v = (_nunvm_read_rc)
log info $"Using version (ansi blue)($v)(ansi reset) from .nvmrc"
$v
} else {
_nunvm_throw "E_TOO_MANY_ARGS" "Only one of `version`, `--lts` and `--latest` must be used at a time"
}
let version_path = (_nunvm_installations | path join $version)
if ($version_path | path exists) {
_nunvm_throw "E_EXISTING_VERSION" $"Nodejs version ($version) already exists. Consider uninstalling it before installing it again"
}
let url = _nunvm_make_url $version
let archive_path = ($nu.temp-path | path join (_nunvm_file_name $version))
log debug $archive_path
log info $"Downloading archive from ($url)"
log info $"Saving file to ($archive_path)"
http get -r $url | save -r -f -p $archive_path
log info "Extracting file from archive"
_nunvm_unarchive $archive_path (_nunvm_installations)
let ext_less_file_name = ((_nunvm_file_name $version) | str replace -r '.zip|.tar|.gz' '')
let archived_dir = (_nunvm_installations | path join $ext_less_file_name)
log debug $"Unarchived directory in ($archived_dir)"
mv $archived_dir (_nunvm_installations | path join $version)
log info "Removing archive file"
try {
rm -f $archive_path
} catch {
log error "Unable to delete archive file due to unexpected reasons. Please do it manually"
}
print $"Successfully installed nodejs (ansi blue)($version)(ansi reset)"
}
# Uninstall a nodejs version
def "main uninstall" [
version: string # Version to uninstall
] {
if not (_nunvm_is_valid_version $version) {
_nunvm_throw "E_INVALID_VERSION" $"($version) is not a valid version string"
}
let version = (_nunvm_prepend_version $version)
let p = (_nunvm_installations | path join $version)
if not ($p | path exists) {
_nunvm_throw "E_VERSION_NOT_INSTALLED" $"($version) is not installed. Cannot uninstall it"
}
try { rm -rf $p } catch { _nunvm_throw "E_EXT_ERROR" "Unable to remove installated version" }
print "Succesfully removed installation"
}
# View version of currently active nodejs
def "main current" [] {
if (which "node" | is-empty) {
print "No version of nodejs currently active"
} else {
node -v
}
}
# Use an installed version
def "main use" [
version: string # the version to use (alias not supported yet)
] {
if not (_nunvm_is_valid_version $version) {
_nunvm_throw "E_INVALID_VERSION" $"($version) is not a valid version string"
}
let version = (_nunvm_prepend_version $version)
let src = (_nunvm_installations | path join $version)
if not ($src | path exists) {
_nunvm_throw "E_VERSION_NOT_INSTALLED" $"($version) is not installed. Cannot uninstall it"
}
let dest = (_nunvm_current)
log info $"Symlinking ($src) to ($dest)"
_nunvm_symln $src $dest
print $"Successfully set ($version) as current"
}
# Show the currently used path value
def "main path" [] {
_nunvm_current
}
# View all installed versions of nodejs
def "main ls" [] {
let installations = _nunvm_installations
if not ($installations | path exists) {
print "No version of nodejs installed"
return
}
ls -s $installations | select name | each { |it| $it.name }
}
# View all available versions of nodejs
def "main ls-remote" [] {
http get 'https://nodejs.org/dist/index.json' | reverse | each { |it| _nunvm_fmt_version $it }
}
# Create an alias
def "main alias" [
version: string # The version to alias
alias: string # Alias name
] {
if not (_nunvm_is_valid_version $version) {
_nunvm_throw "E_INVALID_VERSION" $"($version) is not a valid version string"
}
let version = _nunvm_prepend_version $version
if not (_nunvm_installations | path join $version | path exists) {
_nunvm_throw "E_VERSION_NOT_INSTALLED" $"($version) is not installed. Cannot alias to it"
}
# this is a map of version as value and alias as it's key
let aliases = _nunvm_alias
let aliases = ($aliases | upsert $alias $version)
$aliases | to json -i 4 | save -f (_nunvm_alias_file)
print "Created new alias"
}
# Remove an alias
def "main unalias" [
alias: string # the alias to remove
] {
_nunvm_alias | upsert $alias null | to json -i 4 | save -f (_nunvm_alias_file)
}
# format a version string
def _nunvm_fmt_version [it] {
mut s: string = $it.version
if $it.lts != false {
$s = $"($s) \(($it.lts)\)"
}
$s
}
# get platform
def _nunvm_get_os [] {
$nu.os-info | get name | str downcase
}
# get architecture
def _nunvm_get_arch [] {
let host_arch: string = ($env.NUNVM_ARCH? | default ($nu.os-info | get arch | str downcase))
#FIXME: this is wanky, need to check i*86, not i86
match $host_arch {
"x86_64" | "amd64" => "x64",
"i86" => "x86",
_ => $host_arch,
}
}
# appropiate file name for each platform
def _nunvm_file_name [version: string] {
mut fname = ""
if (_nunvm_get_os | str contains "windows") {
$fname = $"node-($version | str downcase)-win-(_nunvm_get_arch).zip"
} else {
$fname = $"node-($version | str downcase)-(_nunvm_get_os)-(_nunvm_get_arch).tar.gz"
}
echo $fname
}
# form url to download from
def _nunvm_make_url [version: string] {
$"($node_dist)/($version)/(_nunvm_file_name $version)"
}
# throw an error
def _nunvm_throw [tag: string, error: string] {
error make --unspanned {
msg: $"(ansi red_bold)($tag)(ansi reset)\n($error)"
}
}
# read `.nvmrc` from current dir
def _nunvm_read_rc [] {
let v = ([$env.PWD ".nvmrc"] | path join | open | str trim)
if not (_nunvm_is_valid_version $v) {
_nunvm_throw "E_INVALID_VERSION" $".nvmrc contains ($v), which is not a valid version string"
}
$v
}
# lowercase version and if does not start with `v`, add it
def _nunvm_prepend_version [version: string] {
let version = ($version | str downcase)
if not ($version | str starts-with "v") {
$"v($version)"
} else {
$version
}
}
# Path to nunvm home. Use `NUNVM_DIR` env or default to `~/.nunvm`
def _nunvm_home [] {
$env.NUNVM_DIR? | default ($nu.home-path | path join ".nunvm")
}
# $nunvm_home/installations
def _nunvm_installations [] {
_nunvm_home | path join "installations"
}
# Where to store the current version. Use `NUNVM_CURRENT` or default to `$nunvm_home/current`
def _nunvm_current [] {
$env.NUNVM_CURRENT? | default (_nunvm_home | path join "current")
}
def _nunvm_alias_file [] {
[(_nunvm_home) "alias.json"] | path join
}
# json file containing info about aliases
def _nunvm_alias [] {
let p = (_nunvm_alias_file)
# Create parent dir first
let dir = ($p | path dirname)
mkdir $dir
if not ($p | path exists) {
log info "Missing alias file. Creating it"
"{}" | save -f $p
}
# handle case when file exists, but is empty
open $p | default {}
}
# unarchive zip files using platform specific tools
# for windows use powershell's Expand-Archive func, for others
# use tar
def _nunvm_unarchive [
archive_path: string
dest_path: string
] {
match $"(_nunvm_get_os)" {
"windows" => {
powershell -Command $"\"Expand-Archive '($archive_path)' -DestinationPath ($dest_path) -Force\""
}
_ => {
tar xf $archive_path --directory=($dest_path)
}
}
}
# Check if a string is a valid version
# String must start with a `v`
def _nunvm_is_valid_version [version: string] {
# the regex isnt exactly completely correct but it gets the job done
($version | parse -r "^v?([0-9]+).([0-9]+).([0-9]+)$" | is-empty) == false
}
# Get lts version
def _nunvm_get_lts [] {
http get 'https://nodejs.org/dist/index.json' | where { |x| $x.lts != false } | get 0.version
}
# Get latest version
def _nunvm_get_latest [] {
http get 'https://nodejs.org/dist/index.json' | get 0.version
}
# create symlinks. Use `mklink` on windows, otherwise use `ln`
# mklink: https://learn.microsoft.com/windows-server/administration/windows-commands/mklink
# ln: https://www.gnu.org/software/coreutils/ln
def _nunvm_symln [
src: string # the original directory
dest: string # the path to create symlink at
] {
match $"(_nunvm_get_os)" {
"windows" => {
if ($dest | path exists) {
rm $dest
}
mklink /d $dest $src
}
_ => {
ln -sf $src $dest
}
}
}
# Read `$env.NUNVM_LOG` and set it to `$env.NU_LOG_LEVEL`
def-env _nunvm_set_log_level [] {
let nunvm_log = $env.NUNVM_LOG?
if $nunvm_log != null {
$env.NU_LOG_LEVEL = $nunvm_log
}
}